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') run: | set -e TAG="${GITHUB_REF_NAME}" API="https://git.mithraic.cloud/api/v1/repos/ad3laid3/SciCraft/releases" AUTH="Authorization: token ${{ secrets.GITHUB_TOKEN }}" RELEASE_JSON=$(curl -sS -X POST "$API" \ -H "$AUTH" \ -H "Content-Type: application/json" \ -d "{\"tag_name\":\"$TAG\",\"name\":\"SCICRAFT $TAG\",\"body\":\"Automated release built from Gitea Actions.\"}") RELEASE_ID=$(printf '%s' "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])") for f in SciCraft-Modrinth.mrpack SciCraft-CurseForge.zip; do curl -sS -X POST "$API/$RELEASE_ID/assets?name=$(basename "$f")" \ -H "$AUTH" \ -F "attachment=@$f" echo "Uploaded $f" done