ci: remove one-time bundled fallback refresh
This commit is contained in:
@@ -1,312 +0,0 @@
|
||||
name: One-time bundled fallback refresh
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- feature/client-tweaks
|
||||
paths:
|
||||
- .github/workflows/refresh-bundled-fallbacks.yml
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
refresh:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout feature branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: feature/client-tweaks
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Refresh bundled files from upstream
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python - <<'PY'
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import urllib.request
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path.cwd()
|
||||
PAYLOAD = ROOT / "Payload"
|
||||
FALLBACK = PAYLOAD / "Fallback"
|
||||
UA = {"User-Agent": "Dusk-92-Modernization-Tool-Fallback-Refresh"}
|
||||
|
||||
metadata = {"schema": 1, "components": {}}
|
||||
|
||||
def get_bytes(url):
|
||||
req = urllib.request.Request(url, headers=UA)
|
||||
with urllib.request.urlopen(req, timeout=180) as r:
|
||||
return r.read()
|
||||
|
||||
def get_json(url):
|
||||
return json.loads(get_bytes(url).decode("utf-8"))
|
||||
|
||||
def latest(repo):
|
||||
data = get_json(f"https://api.github.com/repos/{repo}/releases/latest")
|
||||
if data.get("draft") or data.get("prerelease"):
|
||||
raise RuntimeError(f"{repo}: latest release is not stable")
|
||||
return data
|
||||
|
||||
def asset(release, exact=None, predicate=None):
|
||||
for a in release.get("assets", []):
|
||||
name = a.get("name", "")
|
||||
if exact and name.lower() == exact.lower():
|
||||
return a
|
||||
if predicate and predicate(name):
|
||||
return a
|
||||
raise RuntimeError(f"Asset not found in {release.get('html_url')}")
|
||||
|
||||
def write_bytes(path, data):
|
||||
path = Path(path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(data)
|
||||
|
||||
def validate_pe(path):
|
||||
data = Path(path).read_bytes()
|
||||
if len(data) < 1024 or data[:2] != b"MZ":
|
||||
raise RuntimeError(f"Invalid PE binary: {path}")
|
||||
|
||||
def extract_zip(data):
|
||||
temp = Path(tempfile.mkdtemp(prefix="modernization_refresh_"))
|
||||
with zipfile.ZipFile(io.BytesIO(data)) as z:
|
||||
for info in z.infolist():
|
||||
dest = (temp / info.filename).resolve()
|
||||
if temp.resolve() not in dest.parents and dest != temp.resolve():
|
||||
raise RuntimeError(f"Unsafe zip path: {info.filename}")
|
||||
z.extractall(temp)
|
||||
return temp
|
||||
|
||||
def find_file(root, name):
|
||||
matches = [p for p in Path(root).rglob("*") if p.is_file() and p.name.lower() == name.lower()]
|
||||
if not matches:
|
||||
raise RuntimeError(f"{name} not found")
|
||||
return matches[0]
|
||||
|
||||
def find_dir(root, name):
|
||||
matches = [p for p in Path(root).rglob("*") if p.is_dir() and p.name.lower() == name.lower()]
|
||||
if not matches:
|
||||
raise RuntimeError(f"{name} directory not found")
|
||||
return matches[0]
|
||||
|
||||
def replace_dir(src, dest):
|
||||
dest = Path(dest)
|
||||
if dest.exists():
|
||||
shutil.rmtree(dest)
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copytree(src, dest)
|
||||
|
||||
def sha256(path):
|
||||
h = hashlib.sha256()
|
||||
with open(path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(1024 * 1024), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
def record(name, source, version, paths):
|
||||
metadata["components"][name] = {
|
||||
"source": source,
|
||||
"version": version,
|
||||
"files": [
|
||||
{
|
||||
"path": str(Path(p).relative_to(ROOT)).replace("\\", "/"),
|
||||
"sha256": sha256(p),
|
||||
"size": Path(p).stat().st_size,
|
||||
}
|
||||
for p in paths if Path(p).is_file()
|
||||
],
|
||||
}
|
||||
|
||||
# Nampower DLL + settings addon
|
||||
rel = latest("brues-code/nampower")
|
||||
a = asset(rel, predicate=lambda n: n.lower().startswith("nampower-") and n.lower().endswith(".zip"))
|
||||
tmp = extract_zip(get_bytes(a["browser_download_url"]))
|
||||
try:
|
||||
dll = find_file(tmp, "nampower.dll")
|
||||
addon = find_dir(tmp, "NampowerSettings")
|
||||
write_bytes(PAYLOAD / "nampower.dll", dll.read_bytes())
|
||||
replace_dir(addon, PAYLOAD / "Interface" / "Addons" / "nampowersettings")
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
validate_pe(PAYLOAD / "nampower.dll")
|
||||
record("Nampower", a["browser_download_url"], rel["tag_name"], [PAYLOAD / "nampower.dll"])
|
||||
|
||||
# UnitXP SP3 DLL + addon
|
||||
rel = latest("brues-code/UnitXP_SP3")
|
||||
a = asset(rel, predicate=lambda n: n.lower().startswith("unitxp_sp3") and n.lower().endswith(".zip"))
|
||||
tmp = extract_zip(get_bytes(a["browser_download_url"]))
|
||||
try:
|
||||
dll = find_file(tmp, "UnitXP_SP3.dll")
|
||||
addon = find_dir(tmp, "UnitXP_SP3_Addon")
|
||||
write_bytes(PAYLOAD / "UnitXP_SP3.dll", dll.read_bytes())
|
||||
replace_dir(addon, PAYLOAD / "Interface" / "Addons" / "UnitXP_SP3_Addon")
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
validate_pe(PAYLOAD / "UnitXP_SP3.dll")
|
||||
record("UnitXP_SP3", a["browser_download_url"], rel["tag_name"], [PAYLOAD / "UnitXP_SP3.dll"])
|
||||
|
||||
# SuperWoW DLL + current SuperAPI master
|
||||
rel = latest("balakethelock/SuperWoW")
|
||||
a = asset(rel, predicate=lambda n: n.lower().startswith("superwow") and n.lower().endswith(".zip"))
|
||||
tmp = extract_zip(get_bytes(a["browser_download_url"]))
|
||||
try:
|
||||
dll = find_file(tmp, "SuperWoWhook.dll")
|
||||
write_bytes(PAYLOAD / "SuperWoWhook.dll", dll.read_bytes())
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
validate_pe(PAYLOAD / "SuperWoWhook.dll")
|
||||
superapi_url = "https://codeload.github.com/balakethelock/SuperAPI/zip/refs/heads/master"
|
||||
tmp = extract_zip(get_bytes(superapi_url))
|
||||
try:
|
||||
addon = find_dir(tmp, "SuperAPI-master")
|
||||
replace_dir(addon, PAYLOAD / "Interface" / "Addons" / "SuperAPI")
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
record("SuperWoW", a["browser_download_url"], rel["tag_name"], [PAYLOAD / "SuperWoWhook.dll"])
|
||||
|
||||
# VanillaHelpers
|
||||
rel = latest("isfir/VanillaHelpers")
|
||||
a = asset(rel, exact="VanillaHelpers.dll")
|
||||
write_bytes(PAYLOAD / "VanillaHelpers.dll", get_bytes(a["browser_download_url"]))
|
||||
validate_pe(PAYLOAD / "VanillaHelpers.dll")
|
||||
record("VanillaHelpers", a["browser_download_url"], rel["tag_name"], [PAYLOAD / "VanillaHelpers.dll"])
|
||||
|
||||
# no1600x1200 current RetroCro backup
|
||||
url = "https://raw.githubusercontent.com/RetroCro/TurtleWoW-Mods/refs/heads/main/Archive/DLL%20BACKUP/no1600x1200.dll"
|
||||
write_bytes(PAYLOAD / "no1600x1200.dll", get_bytes(url))
|
||||
validate_pe(PAYLOAD / "no1600x1200.dll")
|
||||
record("no1600x1200", url, "RetroCro main", [PAYLOAD / "no1600x1200.dll"])
|
||||
|
||||
# PerfBoost current RetroCro binary backup (addon remains the existing avitasia bundle).
|
||||
url = "https://raw.githubusercontent.com/RetroCro/TurtleWoW-Mods/refs/heads/main/Archive/DLL%20BACKUP/perf_boost.dll"
|
||||
write_bytes(PAYLOAD / "perf_boost.dll", get_bytes(url))
|
||||
validate_pe(PAYLOAD / "perf_boost.dll")
|
||||
record("PerfBoost", url, "RetroCro main", [PAYLOAD / "perf_boost.dll"])
|
||||
|
||||
# User-selected WeirdPerformance 0.7.3 build
|
||||
url = "https://codeberg.org/Dusk92/WeirdUtils/releases/download/0.7.3/weirdperformance.dll"
|
||||
write_bytes(PAYLOAD / "weirdperformance.dll", get_bytes(url))
|
||||
validate_pe(PAYLOAD / "weirdperformance.dll")
|
||||
record("WeirdPerformance", url, "0.7.3", [PAYLOAD / "weirdperformance.dll"])
|
||||
|
||||
# ClassicAPI fallback
|
||||
rel = latest("brues-code/ClassicAPI")
|
||||
a = asset(rel, exact="ClassicAPI.dll")
|
||||
write_bytes(FALLBACK / "ClassicAPI.dll", get_bytes(a["browser_download_url"]))
|
||||
validate_pe(FALLBACK / "ClassicAPI.dll")
|
||||
record("ClassicAPI fallback", a["browser_download_url"], rel["tag_name"], [FALLBACK / "ClassicAPI.dll"])
|
||||
|
||||
# AuctionQueryThrottle fallback
|
||||
rel = latest("brues-code/AuctionQueryThrottle")
|
||||
a = asset(rel, exact="AuctionQueryThrottle.dll")
|
||||
write_bytes(FALLBACK / "AuctionQueryThrottle.dll", get_bytes(a["browser_download_url"]))
|
||||
validate_pe(FALLBACK / "AuctionQueryThrottle.dll")
|
||||
record("AuctionQueryThrottle fallback", a["browser_download_url"], rel["tag_name"], [FALLBACK / "AuctionQueryThrottle.dll"])
|
||||
|
||||
# VanillaMultiMonitorFix fallback
|
||||
rel = latest("Mates1500/VanillaMultiMonitorFix")
|
||||
a = asset(rel, exact="release.zip")
|
||||
tmp = extract_zip(get_bytes(a["browser_download_url"]))
|
||||
try:
|
||||
dll = find_file(tmp, "VanillaMultiMonitorFix.dll")
|
||||
cfg = find_file(tmp, "VMMFix_preferred_monitor.txt")
|
||||
out = FALLBACK / "VanillaMultiMonitorFix"
|
||||
if out.exists():
|
||||
shutil.rmtree(out)
|
||||
out.mkdir(parents=True)
|
||||
write_bytes(out / "VanillaMultiMonitorFix.dll", dll.read_bytes())
|
||||
write_bytes(out / "VMMFix_preferred_monitor.txt", cfg.read_bytes())
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
validate_pe(FALLBACK / "VanillaMultiMonitorFix" / "VanillaMultiMonitorFix.dll")
|
||||
record(
|
||||
"VanillaMultiMonitorFix fallback",
|
||||
a["browser_download_url"],
|
||||
rel["tag_name"],
|
||||
[
|
||||
FALLBACK / "VanillaMultiMonitorFix" / "VanillaMultiMonitorFix.dll",
|
||||
FALLBACK / "VanillaMultiMonitorFix" / "VMMFix_preferred_monitor.txt",
|
||||
],
|
||||
)
|
||||
|
||||
# Interact fallback (DLL + addon)
|
||||
rel = latest("lookino/Interact")
|
||||
a = asset(rel, exact="Interact.zip")
|
||||
tmp = extract_zip(get_bytes(a["browser_download_url"]))
|
||||
try:
|
||||
dll = find_file(tmp, "Interact.dll")
|
||||
toc = find_file(tmp, "Interact.toc")
|
||||
addon = toc.parent
|
||||
out = FALLBACK / "Interact"
|
||||
if out.exists():
|
||||
shutil.rmtree(out)
|
||||
out.mkdir(parents=True)
|
||||
write_bytes(out / "Interact.dll", dll.read_bytes())
|
||||
replace_dir(addon, out / "Addon")
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
validate_pe(FALLBACK / "Interact" / "Interact.dll")
|
||||
record("Interact fallback", a["browser_download_url"], rel["tag_name"], [FALLBACK / "Interact" / "Interact.dll"])
|
||||
|
||||
# Tiny sound packs: store complete source trees as offline fallbacks.
|
||||
sound_specs = [
|
||||
("NoErrorSounds", "Macumbafeh/NoErrorSounds", "main", "Sound", FALLBACK / "Audio" / "NoErrorSounds" / "Sound"),
|
||||
("FishPing", "notsureawake/FishPing", "master", "Sound", FALLBACK / "Audio" / "FishPing" / "Sound"),
|
||||
("WarlockMutedDemons", "spzilyk/Warlock-Muted-Demons", "main", "Data", FALLBACK / "Audio" / "WarlockMutedDemons" / "Data"),
|
||||
]
|
||||
for name, repo, branch, source_dir_name, dest in sound_specs:
|
||||
url = f"https://codeload.github.com/{repo}/zip/refs/heads/{branch}"
|
||||
tmp = extract_zip(get_bytes(url))
|
||||
try:
|
||||
src = find_dir(tmp, source_dir_name)
|
||||
replace_dir(src, dest)
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
files = [p for p in dest.rglob("*") if p.is_file()]
|
||||
record(f"{name} fallback", url, branch, files)
|
||||
|
||||
# Keep a modern tubtubs vanilla-tweaks build offline as well.
|
||||
rel = latest("tubtubs/vanilla-tweaks")
|
||||
a = asset(
|
||||
rel,
|
||||
predicate=lambda n: n.lower().endswith(".zip")
|
||||
and "windows" in n.lower()
|
||||
and not n.lower().endswith(".sha256sum"),
|
||||
)
|
||||
tmp = extract_zip(get_bytes(a["browser_download_url"]))
|
||||
try:
|
||||
exe = find_file(tmp, "vanilla-tweaks.exe")
|
||||
write_bytes(ROOT / "vanilla-tweaks.exe", exe.read_bytes())
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
validate_pe(ROOT / "vanilla-tweaks.exe")
|
||||
record("vanilla-tweaks fallback", a["browser_download_url"], rel["tag_name"], [ROOT / "vanilla-tweaks.exe"])
|
||||
|
||||
FALLBACK.mkdir(parents=True, exist_ok=True)
|
||||
(FALLBACK / "versions.json").write_text(
|
||||
json.dumps(metadata, indent=2, sort_keys=True) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
PY
|
||||
|
||||
- name: Commit refreshed bundled fallbacks
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if git diff --quiet; then
|
||||
echo "Bundled files already match upstream."
|
||||
exit 0
|
||||
fi
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add Payload vanilla-tweaks.exe
|
||||
git commit -m "payload: refresh bundled known-good fallbacks [skip ci]"
|
||||
git push origin HEAD:feature/client-tweaks
|
||||
Reference in New Issue
Block a user