Add Gitea Actions CI/CD workflow for pack build and release

Builds the client pack on the media-server docker runner: refreshes the
packwiz index, runs integrity + download-resolvability checks, exports
Modrinth (.mrpack) and CurseForge (.zip) artifacts, validates the
CurseForge manifest completeness, uploads artifacts, and publishes a
release on v* tags.
This commit is contained in:
MiTHRAL 2026-08-19 15:29:15 -04:00
parent 65de1eb5c8
commit 167249fcc8

View file

@ -0,0 +1,146 @@
name: Build & Release Pack
on:
push:
branches: [main]
tags: ['v*']
jobs:
build:
runs-on: docker
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install packwiz
run: go install github.com/packwiz/packwiz@latest
- name: Add packwiz to PATH
run: echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
- name: Refresh index
working-directory: client_pack
run: packwiz refresh
- name: Integrity check
working-directory: client_pack
run: |
python3 - <<'EOF'
import hashlib, os, re, sys, tomllib
index = tomllib.load(open("index.toml", "rb"))
indexed = {e["file"] for e in index["files"]}
mods = [f for f in os.listdir("mods") if f.endswith(".pw.toml")]
missing = [f for f in mods if f"mods/{f}" not in indexed]
if missing:
print("ERROR: pw.toml files missing from index.toml:", missing)
sys.exit(1)
extra = [f for f in indexed if f.startswith("mods/") and f not in {f"mods/{m}" for m in mods}]
if extra:
print("ERROR: index.toml entries missing from mods/:", extra)
sys.exit(1)
errors = 0
for f in mods:
data = tomllib.load(open(f"mods/{f}", "rb"))
d = data.get("download")
if not d or not d.get("hash"):
print(f"ERROR: {f} missing [download] hash")
errors += 1
continue
if not d.get("url") and not d.get("mode", "").startswith("metadata:"):
print(f"ERROR: {f} missing [download] url or metadata mode")
errors += 1
if d.get("url", "").startswith("http://"):
print(f"ERROR: {f} uses insecure http url")
errors += 1
hf = d.get("hash-format", "sha512")
if not re.fullmatch(r"[0-9a-f]+", d["hash"]):
print(f"ERROR: {f} hash not hex ({hf})")
errors += 1
if errors:
sys.exit(1)
print(f"OK: {len(mods)} mods indexed, all have [download] url or metadata source")
EOF
- name: Sanity grep download URLs
working-directory: client_pack
run: |
python3 - <<'EOF'
import glob, sys, tomllib
bad = []
url_count = 0
meta_count = 0
for f in sorted(glob.glob("mods/*.pw.toml")):
d = tomllib.load(open(f, "rb")).get("download", {})
if d.get("url"):
url_count += 1
elif d.get("mode", "").startswith("metadata:"):
meta_count += 1
else:
bad.append(f)
if bad:
print("ERROR: mods without download.url or metadata mode:")
for f in bad:
print(" " + f)
sys.exit(1)
print(f"OK: {url_count} direct urls + {meta_count} metadata sources = all mods resolvable")
EOF
- name: Export Modrinth pack
working-directory: client_pack
run: packwiz modrinth export -o ../SciCraft-Modrinth.mrpack --restrictDomains=false
- name: Export CurseForge pack
working-directory: client_pack
run: packwiz curseforge export -o ../SciCraft-CurseForge.zip
- name: Validate CurseForge pack completeness
working-directory: client_pack
run: |
python3 - <<'EOF'
import json, os, re, sys, zipfile, tomllib
z = zipfile.ZipFile("../SciCraft-CurseForge.zip")
manifest = json.loads(z.read("manifest.json"))
pids = {f["projectID"] for f in manifest["files"]}
ov = {n.split("overrides/mods/")[1] for n in z.namelist() if n.startswith("overrides/mods/")}
missing = []
for f in os.listdir("mods"):
if not f.endswith(".pw.toml"):
continue
data = tomllib.load(open(f"mods/{f}", "rb"))
if data.get("side") == "server":
continue
fn = data["filename"]
cf = data.get("update", {}).get("curseforge", {}).get("project-id")
if cf:
if cf not in pids:
missing.append(fn)
elif fn not in ov:
missing.append(fn)
if missing:
print("ERROR: mods missing from CurseForge export:", missing)
sys.exit(1)
print(f"OK: CurseForge export complete ({len(pids)} manifest files + {len(ov)} overrides)")
EOF
- name: Upload pack artifacts
uses: actions/upload-artifact@v4
with:
name: scicraft-pack
path: |
SciCraft-Modrinth.mrpack
SciCraft-CurseForge.zip
- name: Release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v1
with:
files: |
SciCraft-Modrinth.mrpack
SciCraft-CurseForge.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}