mirror of
https://github.com/Heretek-AI/homebrew-tap.git
synced 2026-11-07 10:56:05 -10:00
feat(ci): implement matrixed nightly CI/CD pipeline, ROCm resolver, and formula overhaul
- Add .github/scripts/resolve_rocm_sdk.sh with AMD TheRock 10.1.0a+ resolver and ETag cache - Add .github/upstream-pins.json and .github/validation-specs.json for 15 managed packages - Add .github/scripts/check_upstreams.py change gate emitting dynamic matrix - Add .github/scripts/validate_bottle.py 3-tier validation harness and proof logging - Add .github/scripts/finalize_run.py and .github/scripts/setup_ccache.sh - Normalize and author all 15 formulas in Formula/ with explicit hardware variants and tests - Author .github/workflows/nightly-matrix.yml and update .github/workflows/tests.yml - Remove obsolete workflows (autobump.yml, build-bottles.yml, test-install-and-run.yml) - Update README.md and add pytest unit/integration test suite
This commit is contained in:
1 parent
5a07a4e671
commit
ed55dd01bb
36 files changed
+3983
-812
No files matched your search
@@ -0,0 +1,424 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
check_upstreams.py: Change Detection & Dynamic Actions Matrix Builder
|
||||
Inspects upstream remotes, formula files, and AMD ROCm nightly builds to emit
|
||||
a precise, deduplicated GitHub Actions execution matrix for Heretek-AI Tap.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import concurrent.futures
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_json(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def save_json(path, data):
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
|
||||
def get_formula_sha256(formula_name, repo_root):
|
||||
formula_path = Path(repo_root) / "Formula" / f"{formula_name}.rb"
|
||||
if not formula_path.is_file():
|
||||
return None
|
||||
with open(formula_path, "rb") as f:
|
||||
return hashlib.sha256(f.read()).hexdigest()
|
||||
|
||||
|
||||
def fetch_remote_commit(remote_url, branch, retries=3):
|
||||
cmd = ["git", "ls-remote", remote_url, f"refs/heads/{branch}"]
|
||||
for attempt in range(1, retries + 1):
|
||||
try:
|
||||
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=20)
|
||||
if res.returncode == 0:
|
||||
lines = res.stdout.strip().splitlines()
|
||||
if lines:
|
||||
sha = lines[0].split()[0]
|
||||
return sha, None
|
||||
return None, f"No refs found for refs/heads/{branch}"
|
||||
err = res.stderr.strip()
|
||||
if attempt == retries:
|
||||
return None, err or f"git ls-remote failed with code {res.returncode}"
|
||||
except Exception as e:
|
||||
if attempt == retries:
|
||||
return None, str(e)
|
||||
time.sleep(attempt * 1.5)
|
||||
return None, "Max retries exceeded"
|
||||
|
||||
|
||||
def fetch_latest_release(repo, token=None, retries=3):
|
||||
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
||||
req = urllib.request.Request(url)
|
||||
req.add_header("User-Agent", "Heretek-Tap-ChangeGate")
|
||||
req.add_header("Accept", "application/vnd.github+json")
|
||||
if token:
|
||||
req.add_header("Authorization", f"Bearer {token}")
|
||||
|
||||
for attempt in range(1, retries + 1):
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
data = json.loads(resp.read().decode("utf-8"))
|
||||
tag = data.get("tag_name")
|
||||
return tag, None
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 404:
|
||||
# Fallback to tags endpoint if no releases are published
|
||||
tags_url = f"https://api.github.com/repos/{repo}/tags?per_page=1"
|
||||
t_req = urllib.request.Request(tags_url)
|
||||
t_req.add_header("User-Agent", "Heretek-Tap-ChangeGate")
|
||||
t_req.add_header("Accept", "application/vnd.github+json")
|
||||
if token:
|
||||
t_req.add_header("Authorization", f"Bearer {token}")
|
||||
try:
|
||||
with urllib.request.urlopen(t_req, timeout=15) as t_resp:
|
||||
tags_data = json.loads(t_resp.read().decode("utf-8"))
|
||||
if tags_data and isinstance(tags_data, list):
|
||||
return tags_data[0].get("name"), None
|
||||
except Exception as ex:
|
||||
return None, str(ex)
|
||||
if attempt == retries:
|
||||
return None, f"HTTP {e.code}: {e.reason}"
|
||||
except Exception as e:
|
||||
if attempt == retries:
|
||||
return None, str(e)
|
||||
time.sleep(attempt * 1.5)
|
||||
return None, "Max retries exceeded"
|
||||
|
||||
|
||||
def resolve_rocm_metadata(repo_root, family="gfx1151", min_version="10.1.0", max_version="", offline_dir=None):
|
||||
if offline_dir:
|
||||
meta_file = Path(offline_dir) / f"rocm_{family}.json"
|
||||
if meta_file.is_file():
|
||||
return load_json(meta_file)
|
||||
|
||||
# Native Python resolution for high speed, reliability, and cross-platform execution
|
||||
index_urls = [
|
||||
"https://nightly.repo.amd.com/rocm/core/tarball/",
|
||||
"https://rocm.nightlies.amd.com/tarball-multi-arch/"
|
||||
]
|
||||
index_content = None
|
||||
resolved_index_url = None
|
||||
|
||||
for url in index_urls:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "Heretek-Tap-ChangeGate"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=12) as resp:
|
||||
content = resp.read().decode("utf-8")
|
||||
if content:
|
||||
index_content = content
|
||||
resolved_index_url = url
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if not index_content:
|
||||
return None
|
||||
|
||||
# Parse files list
|
||||
file_names = []
|
||||
m = re.search(r'const\s+files\s*=\s*(\[.*?\])\s*;', index_content, re.DOTALL)
|
||||
if m:
|
||||
try:
|
||||
entries = json.loads(m.group(1))
|
||||
file_names = [e.get("name", "") for e in entries if isinstance(e, dict)]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not file_names:
|
||||
file_names = list(set(re.findall(r'therock-dist-linux-[A-Za-z0-9_.\-+]+?\.tar\.gz', index_content)))
|
||||
|
||||
pattern = re.compile(
|
||||
r'^therock-dist-linux-' + re.escape(family) +
|
||||
r'-(\d+)\.(\d+)\.(\d+)(a|rc)(\d{8})(?:\+bkc\.(\d{8}))?\.tar\.gz$'
|
||||
)
|
||||
|
||||
def parse_semver(v_str):
|
||||
parts = [int(p) for p in re.findall(r'\d+', v_str)]
|
||||
while len(parts) < 3:
|
||||
parts.append(0)
|
||||
return tuple(parts[:3])
|
||||
|
||||
min_semver = parse_semver(min_version)
|
||||
max_semver = parse_semver(max_version) if max_version else None
|
||||
|
||||
candidates = []
|
||||
for name in file_names:
|
||||
if "-tests-" in name:
|
||||
continue
|
||||
mat = pattern.match(name)
|
||||
if not mat:
|
||||
continue
|
||||
|
||||
maj, minor, patch, stage, stamp, bkc_stamp = mat.groups()
|
||||
semver = (int(maj), int(minor), int(patch))
|
||||
|
||||
if semver < min_semver:
|
||||
continue
|
||||
if max_semver and semver > max_semver:
|
||||
continue
|
||||
|
||||
stage_rank = 0 if stage == "a" else 1
|
||||
sort_key = (semver[0], semver[1], semver[2], stage_rank, int(stamp), int(bkc_stamp or 0))
|
||||
|
||||
candidates.append({
|
||||
"name": name,
|
||||
"version": f"{maj}.{minor}.{patch}{stage}",
|
||||
"build_stamp": stamp,
|
||||
"sort_key": sort_key
|
||||
})
|
||||
|
||||
if not candidates:
|
||||
return None
|
||||
|
||||
candidates.sort(key=lambda x: x["sort_key"])
|
||||
winner = candidates[-1]
|
||||
|
||||
tarball_name = winner["name"]
|
||||
download_url = f"{resolved_index_url.rstrip('/')}/{tarball_name}"
|
||||
etag = ""
|
||||
size = 0
|
||||
last_mod = ""
|
||||
|
||||
# Query HEAD for ETag and size
|
||||
try:
|
||||
h_req = urllib.request.Request(download_url, method="HEAD", headers={"User-Agent": "Heretek-Tap-ChangeGate"})
|
||||
with urllib.request.urlopen(h_req, timeout=10) as h_resp:
|
||||
etag = h_resp.headers.get("ETag", "").strip('"')
|
||||
size_val = h_resp.headers.get("Content-Length", "0")
|
||||
size = int(size_val) if size_val.isdigit() else 0
|
||||
last_mod = h_resp.headers.get("Last-Modified", "")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
"index_url": resolved_index_url,
|
||||
"family": family,
|
||||
"filename": tarball_name,
|
||||
"url": download_url,
|
||||
"version": winner["version"],
|
||||
"build_stamp": winner["build_stamp"],
|
||||
"etag": etag,
|
||||
"size": size,
|
||||
"last_modified": last_mod
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Check upstream changes & build matrix")
|
||||
parser.add_argument("--ledger", default=".github/upstream-pins.json", help="Path to upstream ledger JSON")
|
||||
parser.add_argument("--repo-root", default=".", help="Root of homebrew-tap repository")
|
||||
parser.add_argument("--force", default="", help="Comma-separated formula names to force build, or 'all'")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print summary without writing outputs")
|
||||
parser.add_argument("--offline-fixtures", help="Directory with offline JSON fixtures for testing")
|
||||
parser.add_argument("--token", default=os.getenv("GITHUB_TOKEN"), help="GitHub API Token")
|
||||
args = parser.parse_args()
|
||||
|
||||
repo_root = Path(args.repo_root).resolve()
|
||||
ledger_path = repo_root / args.ledger
|
||||
if not ledger_path.is_file():
|
||||
print(f"Error: Ledger file not found at {ledger_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
ledger = load_json(ledger_path)
|
||||
rocm_cfg = ledger.get("rocm", {})
|
||||
min_rocm_ver = rocm_cfg.get("min_version", "10.1.0")
|
||||
max_rocm_ver = rocm_cfg.get("max_version") or ""
|
||||
packages = ledger.get("packages", {})
|
||||
|
||||
print(f"[*] Querying AMD ROCm nightly toolchain metadata (min_version={min_rocm_ver})...")
|
||||
rocm_meta_gfx1151 = resolve_rocm_metadata(
|
||||
repo_root, family="gfx1151", min_version=min_rocm_ver, max_version=max_rocm_ver, offline_dir=args.offline_fixtures
|
||||
)
|
||||
current_rocm_stamp = rocm_meta_gfx1151.get("build_stamp") if rocm_meta_gfx1151 else None
|
||||
current_rocm_etag = rocm_meta_gfx1151.get("etag") if rocm_meta_gfx1151 else None
|
||||
current_rocm_filename = rocm_meta_gfx1151.get("filename") if rocm_meta_gfx1151 else None
|
||||
current_rocm_index = rocm_meta_gfx1151.get("index_url") if rocm_meta_gfx1151 else None
|
||||
|
||||
print(f" Resolved ROCm nightly build stamp: {current_rocm_stamp} ({current_rocm_filename})")
|
||||
|
||||
# Inspect upstreams in parallel
|
||||
print(f"[*] Checking {len(packages)} tracked packages for upstream changes...")
|
||||
upstream_results = {}
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
|
||||
future_to_pkg = {}
|
||||
for pkg_name, pkg_info in packages.items():
|
||||
track_type = pkg_info.get("track", "commit")
|
||||
if args.offline_fixtures:
|
||||
fixture_file = Path(args.offline_fixtures) / f"{pkg_name}.json"
|
||||
if fixture_file.is_file():
|
||||
data = load_json(fixture_file)
|
||||
upstream_results[pkg_name] = (data.get("target"), None)
|
||||
continue
|
||||
|
||||
if track_type == "commit":
|
||||
future = executor.submit(fetch_remote_commit, pkg_info["remote"], pkg_info["branch"])
|
||||
elif track_type in ("release", "rolling_release", "builder"):
|
||||
future = executor.submit(fetch_latest_release, pkg_info["repo"], args.token)
|
||||
else:
|
||||
future = executor.submit(fetch_remote_commit, pkg_info["remote"], pkg_info.get("branch", "main"))
|
||||
future_to_pkg[future] = pkg_name
|
||||
|
||||
for future in concurrent.futures.as_completed(future_to_pkg):
|
||||
pkg_name = future_to_pkg[future]
|
||||
target_val, err = future.result()
|
||||
upstream_results[pkg_name] = (target_val, err)
|
||||
|
||||
matrix_includes = []
|
||||
hw_matrix_includes = []
|
||||
changed_formulas = set()
|
||||
rocmfpx_needed = False
|
||||
audit_table = []
|
||||
|
||||
force_all = (args.force == "all")
|
||||
forced_set = set(re.split(r"[,\s]+", args.force.strip())) if args.force and not force_all else set()
|
||||
|
||||
for pkg_name, pkg_info in packages.items():
|
||||
formula = pkg_info.get("formula", pkg_name)
|
||||
variants = pkg_info.get("variants", {})
|
||||
current_formula_sha = get_formula_sha256(formula, repo_root)
|
||||
formula_changed = (current_formula_sha and current_formula_sha != pkg_info.get("formula_sha256"))
|
||||
|
||||
target_ref, err = upstream_results.get(pkg_name, (None, "Unknown error"))
|
||||
if err:
|
||||
print(f"[-] Warning: Failed to query {pkg_name}: {err}", file=sys.stderr)
|
||||
|
||||
for var_name, var_info in variants.items():
|
||||
status = var_info.get("status", "enabled")
|
||||
if status != "enabled":
|
||||
audit_table.append({
|
||||
"formula": formula,
|
||||
"variant": var_name,
|
||||
"status": "excluded",
|
||||
"reason": var_info.get("reason", "Disabled in ledger"),
|
||||
"action": "SKIPPED"
|
||||
})
|
||||
continue
|
||||
|
||||
backend = var_info.get("backend", "cpu")
|
||||
last_commit = var_info.get("last_built_commit")
|
||||
last_rocm_stamp = var_info.get("last_rocm_stamp")
|
||||
|
||||
reasons = []
|
||||
if force_all or formula in forced_set or f"{formula}:{var_name}" in forced_set:
|
||||
reasons.append("forced")
|
||||
if target_ref and target_ref != last_commit:
|
||||
reasons.append(f"upstream-update({last_commit[:7] if last_commit else 'none'}->{target_ref[:7]})")
|
||||
if backend == "rocm" and current_rocm_stamp and current_rocm_stamp != last_rocm_stamp:
|
||||
reasons.append(f"rocm-bump({last_rocm_stamp or 'none'}->{current_rocm_stamp})")
|
||||
if formula_changed:
|
||||
reasons.append("formula-rb-edited")
|
||||
|
||||
if reasons:
|
||||
changed_formulas.add(formula)
|
||||
if formula == "rocmfpx":
|
||||
rocmfpx_needed = True
|
||||
|
||||
matrix_entry = {
|
||||
"formula": formula,
|
||||
"package": pkg_name,
|
||||
"variant": var_name,
|
||||
"backend": backend,
|
||||
"default": var_info.get("default", False),
|
||||
"runner": var_info.get("runner", "ubuntu-24.04"),
|
||||
"bottle_tag": var_info.get("bottle_tag", "x86_64_linux"),
|
||||
"brew_flags": var_info.get("brew_flags", ""),
|
||||
"amdgpu_targets": var_info.get("amdgpu_targets", ""),
|
||||
"rocm_family": var_info.get("rocm_family", "gfx1151"),
|
||||
"cuda_version": var_info.get("cuda_version", "12.8"),
|
||||
"hw_validate": var_info.get("hw_validate", False),
|
||||
"target_commit": target_ref or last_commit or "HEAD",
|
||||
"target_rocm_stamp": current_rocm_stamp or "",
|
||||
"target_rocm_filename": current_rocm_filename or "",
|
||||
"target_rocm_etag": current_rocm_etag or "",
|
||||
"reasons": reasons
|
||||
}
|
||||
matrix_includes.append(matrix_entry)
|
||||
|
||||
if var_info.get("hw_validate", False) and backend == "rocm":
|
||||
hw_matrix_includes.append({
|
||||
"formula": formula,
|
||||
"variant": var_name,
|
||||
"target_commit": target_ref or last_commit or "HEAD"
|
||||
})
|
||||
|
||||
audit_table.append({
|
||||
"formula": formula,
|
||||
"variant": var_name,
|
||||
"status": "DIRTY",
|
||||
"reason": ", ".join(reasons),
|
||||
"action": "BUILD"
|
||||
})
|
||||
else:
|
||||
audit_table.append({
|
||||
"formula": formula,
|
||||
"variant": var_name,
|
||||
"status": "UP-TO-DATE",
|
||||
"reason": f"commit={last_commit[:7] if last_commit else 'none'}",
|
||||
"action": "NO-OP"
|
||||
})
|
||||
|
||||
has_work = len(matrix_includes) > 0
|
||||
matrix_output = {"include": matrix_includes}
|
||||
hw_matrix_output = {"include": hw_matrix_includes}
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print(f"{'FORMULA':<22} {'VARIANT':<10} {'STATUS':<12} {'ACTION':<8} {'REASONS / DETAILS'}")
|
||||
print("-" * 80)
|
||||
for row in audit_table:
|
||||
print(f"{row['formula']:<22} {row['variant']:<10} {row['status']:<12} {row['action']:<8} {row['reason']}")
|
||||
print("=" * 80)
|
||||
print(f"Total jobs to execute: {len(matrix_includes)} | ROCmFPX builder needed: {rocmfpx_needed}")
|
||||
|
||||
# Generate step summary markdown
|
||||
summary_lines = [
|
||||
"### Heretek-AI Tap Nightly Matrix Change Gate",
|
||||
f"- **Resolved ROCm Nightly**: `{current_rocm_filename or 'N/A'}` (Stamp: `{current_rocm_stamp or 'N/A'}`)",
|
||||
f"- **Active Jobs Scheduled**: `{len(matrix_includes)}`",
|
||||
f"- **ROCmFPX Re-build Required**: `{rocmfpx_needed}`",
|
||||
"",
|
||||
"| Formula | Variant | Status | Action | Reasons / Details |",
|
||||
"| :--- | :--- | :--- | :--- | :--- |"
|
||||
]
|
||||
for row in audit_table:
|
||||
status_badge = "🟢 Up-to-date" if row['status'] == "UP-TO-DATE" else ("🔴 Rebuild" if row['status'] == "DIRTY" else "⚪ Excluded")
|
||||
summary_lines.append(f"| `{row['formula']}` | `{row['variant']}` | {status_badge} | **{row['action']}** | {row['reason']} |")
|
||||
summary_md = "\n".join(summary_lines)
|
||||
|
||||
step_summary_path = os.getenv("GITHUB_STEP_SUMMARY")
|
||||
if step_summary_path and not args.dry_run:
|
||||
with open(step_summary_path, "a", encoding="utf-8") as f:
|
||||
f.write(summary_md + "\n")
|
||||
|
||||
github_output_path = os.getenv("GITHUB_OUTPUT")
|
||||
if github_output_path and not args.dry_run:
|
||||
with open(github_output_path, "a", encoding="utf-8") as f:
|
||||
f.write(f"has_work={'true' if has_work else 'false'}\n")
|
||||
f.write(f"rocmfpx_needed={'true' if rocmfpx_needed else 'false'}\n")
|
||||
f.write(f"changed_formulas={','.join(sorted(changed_formulas))}\n")
|
||||
f.write(f"matrix={json.dumps(matrix_output)}\n")
|
||||
f.write(f"hw_matrix={json.dumps(hw_matrix_output)}\n")
|
||||
f.write(f"rocm_stamp={current_rocm_stamp or ''}\n")
|
||||
f.write(f"rocm_filename={current_rocm_filename or ''}\n")
|
||||
f.write(f"rocm_etag={current_rocm_etag or ''}\n")
|
||||
f.write(f"rocm_index={current_rocm_index or ''}\n")
|
||||
|
||||
if args.dry_run:
|
||||
print("\n[DRY RUN] Generated Matrix:")
|
||||
print(json.dumps(matrix_output, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
finalize_run.py: Validation Aggregator & State Ledger Synchronizer
|
||||
Parses all generated validation proofs, verifies cryptographic assertions,
|
||||
synchronizes .github/upstream-pins.json with newly verified commit SHAs &
|
||||
ROCm build stamps, and prepares assets for GitHub Releases publication.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_json(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def save_json(path, data):
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
|
||||
def compute_file_sha256(filepath):
|
||||
h = hashlib.sha256()
|
||||
with open(filepath, "rb") as f:
|
||||
while chunk := f.read(65536):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Synchronize ledger with validation proofs")
|
||||
parser.add_argument("--ledger", default=".github/upstream-pins.json", help="Path to upstream ledger JSON")
|
||||
parser.add_argument("--repo-root", default=".", help="Root of repository")
|
||||
parser.add_argument("--proofs-dir", default=".", help="Directory containing validation-proof-*.json")
|
||||
parser.add_argument("--rocm-stamp", default="", help="Active ROCm nightly build stamp")
|
||||
parser.add_argument("--rocm-index", default="", help="Active ROCm nightly index URL")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Do not write ledger changes")
|
||||
args = parser.parse_args()
|
||||
|
||||
repo_root = Path(args.repo_root).resolve()
|
||||
ledger_path = repo_root / args.ledger
|
||||
if not ledger_path.is_file():
|
||||
print(f"Error: Ledger file not found at {ledger_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
ledger = load_json(ledger_path)
|
||||
proofs_dir = Path(args.proofs_dir).resolve()
|
||||
|
||||
proof_files = list(proofs_dir.glob("**/validation-proof-*.json"))
|
||||
print(f"[*] Found {len(proof_files)} validation proofs to aggregate...")
|
||||
|
||||
results_table = []
|
||||
passed_count = 0
|
||||
failed_count = 0
|
||||
now_utc = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
|
||||
|
||||
for pf in sorted(proof_files):
|
||||
try:
|
||||
data = load_json(pf)
|
||||
except Exception as e:
|
||||
print(f"[-] Error reading proof {pf}: {e}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
formula = data.get("formula")
|
||||
variant = data.get("variant")
|
||||
overall_status = data.get("overall_status")
|
||||
target_commit = data.get("target_commit")
|
||||
|
||||
if not formula or not variant:
|
||||
continue
|
||||
|
||||
is_passed = overall_status in ("PASSED", "PASSED_HW_ABSENT")
|
||||
if is_passed:
|
||||
passed_count += 1
|
||||
# Update ledger entry
|
||||
pkg_data = ledger.get("packages", {}).get(formula, {})
|
||||
var_data = pkg_data.get("variants", {}).get(variant, {})
|
||||
if var_data:
|
||||
if target_commit:
|
||||
var_data["last_built_commit"] = target_commit
|
||||
if var_data.get("backend") == "rocm" and args.rocm_stamp:
|
||||
var_data["last_rocm_stamp"] = args.rocm_stamp
|
||||
var_data["last_success_at"] = now_utc
|
||||
|
||||
# Update formula sha256
|
||||
formula_file = repo_root / "Formula" / f"{formula}.rb"
|
||||
if formula_file.is_file():
|
||||
pkg_data["formula_sha256"] = compute_file_sha256(formula_file)
|
||||
else:
|
||||
failed_count += 1
|
||||
|
||||
results_table.append({
|
||||
"formula": formula,
|
||||
"variant": variant,
|
||||
"status": overall_status,
|
||||
"proof_sha256": data.get("proof_sha256", "N/A"),
|
||||
"file": pf.name
|
||||
})
|
||||
|
||||
# Update top-level ROCm state in ledger
|
||||
if args.rocm_stamp:
|
||||
ledger["rocm"]["last_build_stamp"] = args.rocm_stamp
|
||||
if args.rocm_index:
|
||||
ledger["rocm"]["last_index_url"] = args.rocm_index
|
||||
|
||||
if not args.dry_run and passed_count > 0:
|
||||
print(f"[*] Writing updated ledger state to {ledger_path}...")
|
||||
save_json(ledger_path, ledger)
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print(f"{'FORMULA':<22} {'VARIANT':<10} {'STATUS':<18} {'PROOF SHA256'}")
|
||||
print("-" * 80)
|
||||
for row in results_table:
|
||||
print(f"{row['formula']:<22} {row['variant']:<10} {row['status']:<18} {row['proof_sha256'][:16]}...")
|
||||
print("=" * 80)
|
||||
print(f"Aggregation complete: {passed_count} Passed | {failed_count} Failed")
|
||||
|
||||
# Generate step summary markdown
|
||||
summary_lines = [
|
||||
"### Nightly Bottle Validation & Ledger Update Summary",
|
||||
f"- **Aggregation Timestamp**: `{now_utc}`",
|
||||
f"- **Active ROCm Nightly Stamp**: `{args.rocm_stamp or 'N/A'}`",
|
||||
f"- **Total Validated Variants**: `{len(results_table)}` (`{passed_count}` Passed, `{failed_count}` Failed)",
|
||||
"",
|
||||
"| Formula | Variant | Overall Status | Proof SHA256 |",
|
||||
"| :--- | :--- | :--- | :--- |"
|
||||
]
|
||||
for row in results_table:
|
||||
badge = "🟢 " + row['status'] if "PASSED" in row['status'] else "🔴 " + row['status']
|
||||
summary_lines.append(f"| `{row['formula']}` | `{row['variant']}` | {badge} | `{row['proof_sha256'][:24]}...` |")
|
||||
|
||||
summary_md = "\n".join(summary_lines)
|
||||
step_summary_path = os.getenv("GITHUB_STEP_SUMMARY")
|
||||
if step_summary_path and not args.dry_run:
|
||||
with open(step_summary_path, "a", encoding="utf-8") as f:
|
||||
f.write(summary_md + "\n")
|
||||
|
||||
github_output_path = os.getenv("GITHUB_OUTPUT")
|
||||
if github_output_path and not args.dry_run:
|
||||
with open(github_output_path, "a", encoding="utf-8") as f:
|
||||
f.write(f"passed_count={passed_count}\n")
|
||||
f.write(f"failed_count={failed_count}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,456 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# resolve_rocm_sdk.sh: Dynamic AMD ROCm TheRock Multi-Arch / APU SDK Resolver
|
||||
#
|
||||
# Queries AMD ROCm nightly index, resolves the latest 10.1.0a+ release for the
|
||||
# specified GPU architecture family, manages local tarball caches via HTTP ETag,
|
||||
# unpacks into a unified prefix, and exports compiler / toolchain environment vars.
|
||||
# ==============================================================================
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
DEFAULT_PRIMARY_INDEX="https://nightly.repo.amd.com/rocm/core/tarball/"
|
||||
DEFAULT_LEGACY_INDEX="https://rocm.nightlies.amd.com/tarball-multi-arch/"
|
||||
|
||||
FAMILY="gfx1151"
|
||||
CHANNEL="nightly"
|
||||
MIN_VERSION="10.1.0"
|
||||
MAX_VERSION=""
|
||||
PREFIX="/opt/rocm-nightly"
|
||||
CACHE_DIR="${HOME}/.cache/rocm-nightly"
|
||||
RESOLVE_ONLY="false"
|
||||
OUTPUT_JSON="false"
|
||||
INDEX_FILE=""
|
||||
INDEX_URLS=()
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
Options:
|
||||
--family <name> GPU target family (multiarch, gfx1151, gfx1150, gfx110X-all, gfx90a, gfx94X-dcgpu) [default: gfx1151]
|
||||
--channel <channel> Release channel (nightly, bkc) [default: nightly]
|
||||
--min-version <ver> Minimum ROCm semantic version to accept [default: 10.1.0]
|
||||
--max-version <ver> Optional maximum ROCm semantic version upper bound
|
||||
--prefix <path> Installation prefix directory [default: /opt/rocm-nightly]
|
||||
--cache-dir <path> Directory to cache downloaded tarballs and manifests [default: ~/.cache/rocm-nightly]
|
||||
--index-url <url> Override index URL (can be specified multiple times; checked in order)
|
||||
--index-file <file> Path to local HTML index file (for offline testing / fixtures)
|
||||
--resolve-only Only query and resolve the newest matching tarball without downloading
|
||||
--json Output resolved metadata as JSON
|
||||
-h, --help Show this help message
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--family)
|
||||
FAMILY="$2"; shift 2 ;;
|
||||
--channel)
|
||||
CHANNEL="$2"; shift 2 ;;
|
||||
--min-version)
|
||||
MIN_VERSION="$2"; shift 2 ;;
|
||||
--max-version)
|
||||
MAX_VERSION="$2"; shift 2 ;;
|
||||
--prefix)
|
||||
PREFIX="$2"; shift 2 ;;
|
||||
--cache-dir)
|
||||
CACHE_DIR="$2"; shift 2 ;;
|
||||
--index-url)
|
||||
INDEX_URLS+=("$2"); shift 2 ;;
|
||||
--index-file)
|
||||
INDEX_FILE="$2"; shift 2 ;;
|
||||
--resolve-only)
|
||||
RESOLVE_ONLY="true"; shift ;;
|
||||
--json)
|
||||
OUTPUT_JSON="true"; shift ;;
|
||||
-h|--help)
|
||||
usage ;;
|
||||
*)
|
||||
echo "::error::Unknown argument: $1" >&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ${#INDEX_URLS[@]} -eq 0 ]]; then
|
||||
INDEX_URLS=("$DEFAULT_PRIMARY_INDEX" "$DEFAULT_LEGACY_INDEX")
|
||||
fi
|
||||
|
||||
mkdir -p "$CACHE_DIR"
|
||||
|
||||
PYTHON_CMD="python3"
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
if command -v python >/dev/null 2>&1; then
|
||||
PYTHON_CMD="python"
|
||||
else
|
||||
echo "::error::python3 is required to parse ROCm index metadata" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 1. Fetch index and parse candidate files
|
||||
# ------------------------------------------------------------------------------
|
||||
RESOLVED_INDEX_URL=""
|
||||
INDEX_CONTENT=""
|
||||
|
||||
if [[ -n "$INDEX_FILE" ]]; then
|
||||
if [[ ! -f "$INDEX_FILE" ]]; then
|
||||
echo "::error::Specified index file not found: $INDEX_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
INDEX_CONTENT="$(cat "$INDEX_FILE")"
|
||||
RESOLVED_INDEX_URL="file://$INDEX_FILE"
|
||||
else
|
||||
for test_url in "${INDEX_URLS[@]}"; do
|
||||
if content=$(curl -fsSL --retry 3 --connect-timeout 10 "$test_url" 2>/dev/null); then
|
||||
if [[ -n "$content" ]]; then
|
||||
INDEX_CONTENT="$content"
|
||||
RESOLVED_INDEX_URL="$test_url"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -z "$INDEX_CONTENT" ]]; then
|
||||
echo "::error::Failed to retrieve ROCm index from any configured endpoint." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 2. Extract, filter, and sort newest tarball matching constraints
|
||||
# ------------------------------------------------------------------------------
|
||||
PARSE_SCRIPT=$(cat <<'EOF'
|
||||
import sys, re, json
|
||||
|
||||
family = sys.argv[1]
|
||||
channel = sys.argv[2]
|
||||
min_ver = sys.argv[3]
|
||||
max_ver = sys.argv[4] if len(sys.argv) > 4 else ""
|
||||
|
||||
content = sys.stdin.read()
|
||||
|
||||
# 1. Try parsing embedded JSON: const files = [...];
|
||||
file_names = []
|
||||
m = re.search(r'const\s+files\s*=\s*(\[.*?\])\s*;', content, re.DOTALL)
|
||||
if m:
|
||||
try:
|
||||
entries = json.loads(m.group(1))
|
||||
file_names = [e.get("name", "") for e in entries if isinstance(e, dict)]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. Fallback to extracting all tar.gz matches
|
||||
if not file_names:
|
||||
file_names = list(set(re.findall(r'therock-dist-linux-[A-Za-z0-9_.\-+]+?\.tar\.gz', content)))
|
||||
|
||||
# Regex pattern matching linux distro tarballs for target family
|
||||
# Examples:
|
||||
# therock-dist-linux-gfx1151-10.3.0a20261105.tar.gz
|
||||
# therock-dist-linux-multiarch-10.1.0a20260811+bkc.20260820.tar.gz
|
||||
pattern = re.compile(
|
||||
r'^therock-dist-linux-' + re.escape(family) +
|
||||
r'-(\d+)\.(\d+)\.(\d+)(a|rc)(\d{8})(?:\+bkc\.(\d{8}))?\.tar\.gz$'
|
||||
)
|
||||
|
||||
candidates = []
|
||||
|
||||
def parse_semver(v_str):
|
||||
parts = [int(p) for p in re.findall(r'\d+', v_str)]
|
||||
while len(parts) < 3:
|
||||
parts.append(0)
|
||||
return tuple(parts[:3])
|
||||
|
||||
min_semver = parse_semver(min_ver)
|
||||
max_semver = parse_semver(max_ver) if max_ver else None
|
||||
|
||||
for name in file_names:
|
||||
if "-tests-" in name:
|
||||
continue
|
||||
mat = pattern.match(name)
|
||||
if not mat:
|
||||
continue
|
||||
|
||||
maj, minor, patch, stage, stamp, bkc_stamp = mat.groups()
|
||||
semver = (int(maj), int(minor), int(patch))
|
||||
|
||||
if semver < min_semver:
|
||||
continue
|
||||
if max_semver and semver > max_semver:
|
||||
continue
|
||||
|
||||
is_bkc = bool(bkc_stamp)
|
||||
if channel == "nightly" and is_bkc:
|
||||
continue
|
||||
elif channel == "bkc" and not is_bkc:
|
||||
continue
|
||||
|
||||
stage_rank = 0 if stage == "a" else 1
|
||||
sort_key = (semver[0], semver[1], semver[2], stage_rank, int(stamp), int(bkc_stamp or 0))
|
||||
|
||||
candidates.append({
|
||||
"name": name,
|
||||
"version": f"{maj}.{minor}.{patch}{stage}",
|
||||
"build_stamp": stamp,
|
||||
"bkc_stamp": bkc_stamp or "",
|
||||
"sort_key": sort_key
|
||||
})
|
||||
|
||||
if not candidates:
|
||||
sys.exit(2)
|
||||
|
||||
candidates.sort(key=lambda x: x["sort_key"])
|
||||
winner = candidates[-1]
|
||||
|
||||
print(json.dumps({
|
||||
"filename": winner["name"],
|
||||
"version": winner["version"],
|
||||
"build_stamp": winner["build_stamp"],
|
||||
"bkc_stamp": winner["bkc_stamp"],
|
||||
"candidate_count": len(candidates)
|
||||
}))
|
||||
EOF
|
||||
)
|
||||
|
||||
PARSED_RESULT=$(echo "$INDEX_CONTENT" | "$PYTHON_CMD" -c "$PARSE_SCRIPT" "$FAMILY" "$CHANNEL" "$MIN_VERSION" "$MAX_VERSION" || true)
|
||||
|
||||
if [[ -z "$PARSED_RESULT" ]]; then
|
||||
echo "::error::No matching ROCm SDK tarball found for family='$FAMILY', min_version='$MIN_VERSION', channel='$CHANNEL'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARBALL_NAME=$("$PYTHON_CMD" -c "import sys, json; print(json.loads(sys.argv[1])['filename'])" "$PARSED_RESULT")
|
||||
ROCM_VERSION=$("$PYTHON_CMD" -c "import sys, json; print(json.loads(sys.argv[1])['version'])" "$PARSED_RESULT")
|
||||
BUILD_STAMP=$("$PYTHON_CMD" -c "import sys, json; print(json.loads(sys.argv[1])['build_stamp'])" "$PARSED_RESULT")
|
||||
|
||||
# Clean base URL ending in /
|
||||
BASE_URL="${RESOLVED_INDEX_URL%/}/"
|
||||
DOWNLOAD_URL="${BASE_URL}${TARBALL_NAME}"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 3. Query remote metadata (ETag, Content-Length, Last-Modified)
|
||||
# ------------------------------------------------------------------------------
|
||||
REMOTE_ETAG=""
|
||||
REMOTE_SIZE=""
|
||||
REMOTE_LAST_MODIFIED=""
|
||||
|
||||
if [[ "$RESOLVED_INDEX_URL" =~ ^http ]]; then
|
||||
HEADERS=$(curl -sI -L --retry 3 --connect-timeout 10 "$DOWNLOAD_URL" 2>/dev/null || true)
|
||||
REMOTE_ETAG=$(echo "$HEADERS" | grep -i '^etag:' | head -n1 | awk '{print $2}' | tr -d '\r"' || true)
|
||||
REMOTE_SIZE=$(echo "$HEADERS" | grep -i '^content-length:' | head -n1 | awk '{print $2}' | tr -d '\r' || true)
|
||||
REMOTE_LAST_MODIFIED=$(echo "$HEADERS" | grep -i '^last-modified:' | head -n1 | cut -d':' -f2- | sed 's/^[ \t]*//' | tr -d '\r' || true)
|
||||
fi
|
||||
|
||||
AGE_DAYS=0
|
||||
if [[ "$BUILD_STAMP" =~ ^[0-9]{8}$ ]]; then
|
||||
CURRENT_YEAR=$(date -u +%Y)
|
||||
CURRENT_MONTH=$(date -u +%m)
|
||||
CURRENT_DAY=$(date -u +%d)
|
||||
CURRENT_SEC=$(date -u -d "${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DAY}" +%s 2>/dev/null || date -u -j -f "%Y%m%d" "${CURRENT_YEAR}${CURRENT_MONTH}${CURRENT_DAY}" +%s 2>/dev/null || echo 0)
|
||||
STAMP_SEC=$(date -u -d "${BUILD_STAMP:0:4}-${BUILD_STAMP:4:2}-${BUILD_STAMP:6:2}" +%s 2>/dev/null || date -u -j -f "%Y%m%d" "$BUILD_STAMP" +%s 2>/dev/null || echo 0)
|
||||
if [[ "$CURRENT_SEC" -gt 0 && "$STAMP_SEC" -gt 0 ]]; then
|
||||
DIFF_SEC=$(( CURRENT_SEC - STAMP_SEC ))
|
||||
AGE_DAYS=$(( DIFF_SEC / 86400 ))
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$OUTPUT_JSON" == "true" || "$RESOLVE_ONLY" == "true" ]]; then
|
||||
RESOLVE_JSON=$("$PYTHON_CMD" -c "
|
||||
import sys, json
|
||||
data = {
|
||||
'index_url': sys.argv[1],
|
||||
'family': sys.argv[2],
|
||||
'filename': sys.argv[3],
|
||||
'url': sys.argv[4],
|
||||
'version': sys.argv[5],
|
||||
'build_stamp': sys.argv[6],
|
||||
'etag': sys.argv[7],
|
||||
'size': int(sys.argv[8]) if sys.argv[8].isdigit() else 0,
|
||||
'last_modified': sys.argv[9],
|
||||
'age_days': int(sys.argv[10])
|
||||
}
|
||||
print(json.dumps(data, indent=2))
|
||||
" "$RESOLVED_INDEX_URL" "$FAMILY" "$TARBALL_NAME" "$DOWNLOAD_URL" "$ROCM_VERSION" "$BUILD_STAMP" "$REMOTE_ETAG" "${REMOTE_SIZE:-0}" "$REMOTE_LAST_MODIFIED" "$AGE_DAYS")
|
||||
fi
|
||||
|
||||
if [[ "$RESOLVE_ONLY" == "true" ]]; then
|
||||
if [[ "$OUTPUT_JSON" == "true" ]]; then
|
||||
echo "$RESOLVE_JSON"
|
||||
else
|
||||
echo "Resolved ROCm SDK: $TARBALL_NAME from $DOWNLOAD_URL ($BUILD_STAMP)"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Warn if legacy index was used or age is suspicious
|
||||
if [[ "$RESOLVED_INDEX_URL" == *"$DEFAULT_LEGACY_INDEX"* ]]; then
|
||||
echo "::warning::ROCm SDK resolved from legacy index endpoint: $RESOLVED_INDEX_URL"
|
||||
fi
|
||||
if [[ $AGE_DAYS -gt 3 ]]; then
|
||||
echo "::warning::Latest ROCm SDK nightly build is $AGE_DAYS days old ($BUILD_STAMP)"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 4. Cache & Download Handling
|
||||
# ------------------------------------------------------------------------------
|
||||
TARGET_TARBALL="${CACHE_DIR}/${TARBALL_NAME}"
|
||||
MANIFEST_FILE="${PREFIX}/.therock-manifest.json"
|
||||
|
||||
NEEDS_DOWNLOAD="true"
|
||||
if [[ -f "$TARGET_TARBALL" ]]; then
|
||||
if [[ -n "$REMOTE_ETAG" && -f "${TARGET_TARBALL}.etag" ]]; then
|
||||
CACHED_ETAG=$(cat "${TARGET_TARBALL}.etag")
|
||||
if [[ "$CACHED_ETAG" == "$REMOTE_ETAG" ]]; then
|
||||
NEEDS_DOWNLOAD="false"
|
||||
fi
|
||||
elif [[ -z "$REMOTE_ETAG" ]]; then
|
||||
NEEDS_DOWNLOAD="false"
|
||||
fi
|
||||
fi
|
||||
|
||||
ALREADY_INSTALLED="false"
|
||||
if [[ -d "$PREFIX" && -f "$MANIFEST_FILE" ]]; then
|
||||
INSTALLED_FILENAME=$("$PYTHON_CMD" -c "import sys, json; print(json.load(open(sys.argv[1])).get('filename', ''))" "$MANIFEST_FILE" 2>/dev/null || true)
|
||||
INSTALLED_ETAG=$("$PYTHON_CMD" -c "import sys, json; print(json.load(open(sys.argv[1])).get('etag', ''))" "$MANIFEST_FILE" 2>/dev/null || true)
|
||||
if [[ "$INSTALLED_FILENAME" == "$TARBALL_NAME" ]]; then
|
||||
if [[ -z "$REMOTE_ETAG" || "$INSTALLED_ETAG" == "$REMOTE_ETAG" ]]; then
|
||||
ALREADY_INSTALLED="true"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$ALREADY_INSTALLED" == "true" ]]; then
|
||||
echo "ROCm SDK $TARBALL_NAME is already provisioned at $PREFIX"
|
||||
else
|
||||
if [[ "$NEEDS_DOWNLOAD" == "true" ]]; then
|
||||
echo "Downloading $TARBALL_NAME from $DOWNLOAD_URL..."
|
||||
TMP_DOWNLOAD="${TARGET_TARBALL}.tmp"
|
||||
curl -fL --retry 5 --retry-all-errors -C - -o "$TMP_DOWNLOAD" "$DOWNLOAD_URL"
|
||||
mv "$TMP_DOWNLOAD" "$TARGET_TARBALL"
|
||||
if [[ -n "$REMOTE_ETAG" ]]; then
|
||||
echo "$REMOTE_ETAG" > "${TARGET_TARBALL}.etag"
|
||||
fi
|
||||
else
|
||||
echo "Using cached tarball: $TARGET_TARBALL"
|
||||
fi
|
||||
|
||||
echo "Unpacking $TARGET_TARBALL into $PREFIX..."
|
||||
SUDO=""
|
||||
if [[ ! -w "$(dirname "$PREFIX")" && "$(id -u)" -ne 0 ]]; then
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
SUDO="sudo"
|
||||
fi
|
||||
fi
|
||||
|
||||
$SUDO mkdir -p "$PREFIX"
|
||||
TAR_OPTS=("-xzf" "$TARGET_TARBALL" "-C" "$PREFIX")
|
||||
if command -v pigz >/dev/null 2>&1; then
|
||||
TAR_OPTS=("--use-compress-program=pigz" "-xf" "$TARGET_TARBALL" "-C" "$PREFIX")
|
||||
fi
|
||||
|
||||
$SUDO tar "${TAR_OPTS[@]}"
|
||||
|
||||
# Write installation manifest
|
||||
TMP_MANIFEST=$(mktemp)
|
||||
"$PYTHON_CMD" -c "
|
||||
import sys, json
|
||||
data = {
|
||||
'filename': sys.argv[1],
|
||||
'version': sys.argv[2],
|
||||
'build_stamp': sys.argv[3],
|
||||
'etag': sys.argv[4],
|
||||
'index_url': sys.argv[5],
|
||||
'family': sys.argv[6]
|
||||
}
|
||||
with open(sys.argv[7], 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
" "$TARBALL_NAME" "$ROCM_VERSION" "$BUILD_STAMP" "$REMOTE_ETAG" "$RESOLVED_INDEX_URL" "$FAMILY" "$TMP_MANIFEST"
|
||||
|
||||
$SUDO mv "$TMP_MANIFEST" "$MANIFEST_FILE"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 5. Toolchain Path Normalization
|
||||
# ------------------------------------------------------------------------------
|
||||
SUDO=""
|
||||
if [[ ! -w "$PREFIX" && "$(id -u)" -ne 0 ]]; then
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
SUDO="sudo"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Symlink llvm directory if under lib/llvm
|
||||
if [[ -d "${PREFIX}/lib/llvm" && ! -d "${PREFIX}/llvm" ]]; then
|
||||
$SUDO ln -sfn "${PREFIX}/lib/llvm" "${PREFIX}/llvm"
|
||||
elif [[ -d "${PREFIX}/llvm" && ! -d "${PREFIX}/lib/llvm" ]]; then
|
||||
$SUDO mkdir -p "${PREFIX}/lib"
|
||||
$SUDO ln -sfn "${PREFIX}/llvm" "${PREFIX}/lib/llvm"
|
||||
fi
|
||||
|
||||
# Ensure bin directory exists
|
||||
$SUDO mkdir -p "${PREFIX}/bin"
|
||||
|
||||
CLANGXX=""
|
||||
for candidate in \
|
||||
"${PREFIX}/llvm/bin/clang++" \
|
||||
"${PREFIX}/lib/llvm/bin/clang++" \
|
||||
"${PREFIX}/bin/amdclang++" \
|
||||
"${PREFIX}/bin/clang++"; do
|
||||
if [[ -x "$candidate" ]]; then
|
||||
CLANGXX="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "$CLANGXX" ]]; then
|
||||
[[ ! -e "${PREFIX}/bin/hipcc" ]] && $SUDO ln -sf "$CLANGXX" "${PREFIX}/bin/hipcc" || true
|
||||
[[ ! -e "${PREFIX}/bin/amdclang++" ]] && $SUDO ln -sf "$CLANGXX" "${PREFIX}/bin/amdclang++" || true
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 6. Export Environment Variables
|
||||
# ------------------------------------------------------------------------------
|
||||
HIP_CLANG_PATH="${PREFIX}/llvm/bin"
|
||||
[[ ! -d "$HIP_CLANG_PATH" && -d "${PREFIX}/lib/llvm/bin" ]] && HIP_CLANG_PATH="${PREFIX}/lib/llvm/bin"
|
||||
|
||||
EXPORT_ENV=(
|
||||
"ROCM_PATH=${PREFIX}"
|
||||
"HIP_PATH=${PREFIX}"
|
||||
"HOMEBREW_ROCM_PATH=${PREFIX}"
|
||||
"HIP_CLANG_PATH=${HIP_CLANG_PATH}"
|
||||
)
|
||||
|
||||
if [[ -n "$CLANGXX" ]]; then
|
||||
EXPORT_ENV+=("CMAKE_HIP_COMPILER=${CLANGXX}")
|
||||
fi
|
||||
|
||||
LIB_PATHS=()
|
||||
for p in "${PREFIX}/lib" "${PREFIX}/lib64" "${PREFIX}/llvm/lib"; do
|
||||
[[ -d "$p" ]] && LIB_PATHS+=("$p")
|
||||
done
|
||||
LD_SEARCH=$(IFS=:; echo "${LIB_PATHS[*]}")
|
||||
EXPORT_ENV+=("LD_LIBRARY_PATH=${LD_SEARCH}:\${LD_LIBRARY_PATH:-}")
|
||||
|
||||
if [[ -n "${GITHUB_ENV:-}" && -w "$GITHUB_ENV" ]]; then
|
||||
for env_line in "${EXPORT_ENV[@]}"; do
|
||||
echo "$env_line" >> "$GITHUB_ENV"
|
||||
done
|
||||
echo "${PREFIX}/bin" >> "$GITHUB_PATH"
|
||||
echo "$HIP_CLANG_PATH" >> "$GITHUB_PATH"
|
||||
fi
|
||||
|
||||
if [[ "$OUTPUT_JSON" == "true" ]]; then
|
||||
echo "$RESOLVE_JSON"
|
||||
else
|
||||
cat <<ENV_OUTPUT
|
||||
# ROCm Environment configured successfully:
|
||||
export ROCM_PATH="${PREFIX}"
|
||||
export HIP_PATH="${PREFIX}"
|
||||
export HOMEBREW_ROCM_PATH="${PREFIX}"
|
||||
export HIP_CLANG_PATH="${HIP_CLANG_PATH}"
|
||||
export PATH="${PREFIX}/bin:${HIP_CLANG_PATH}:\$PATH"
|
||||
export LD_LIBRARY_PATH="${LD_SEARCH}:\${LD_LIBRARY_PATH:-}"
|
||||
ENV_OUTPUT
|
||||
fi
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# setup_ccache.sh: Compiler Cache Provisioning for Homebrew Tap CI/CD Runners
|
||||
# Configures high-performance ccache directory backed by RAM or fast runner SSD.
|
||||
# ==============================================================================
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
CCACHE_SIZE="${1:-6G}"
|
||||
BREW_CACHE_DIR="$(brew --cache 2>/dev/null || echo "${HOME}/.cache/Homebrew")"
|
||||
CCACHE_DIR="${BREW_CACHE_DIR}/ccache"
|
||||
|
||||
echo "[*] Configuring ccache at ${CCACHE_DIR} (Target Size: ${CCACHE_SIZE})..."
|
||||
mkdir -p "$CCACHE_DIR"
|
||||
|
||||
if [[ "$(uname -s)" == "Linux" ]]; then
|
||||
# On Linux runners, mount tmpfs if running as root or have passwordless sudo
|
||||
if [[ -w "/dev/shm" ]]; then
|
||||
SHM_CCACHE="/dev/shm/homebrew-ccache"
|
||||
mkdir -p "$SHM_CCACHE"
|
||||
# Sync from persistent ccache directory if exists
|
||||
if [[ -d "$CCACHE_DIR" && "$(ls -A "$CCACHE_DIR" 2>/dev/null)" ]]; then
|
||||
cp -a "${CCACHE_DIR}/." "$SHM_CCACHE/" 2>/dev/null || true
|
||||
fi
|
||||
CCACHE_DIR="$SHM_CCACHE"
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v ccache >/dev/null 2>&1; then
|
||||
export CCACHE_DIR="$CCACHE_DIR"
|
||||
ccache --set-config="max_size=${CCACHE_SIZE}"
|
||||
ccache --set-config="compression=true"
|
||||
ccache --set-config="compression_level=6"
|
||||
ccache --set-config="sloppiness=time_macros,file_macro,include_file_mtime"
|
||||
ccache --zero-stats
|
||||
ccache --show-stats
|
||||
fi
|
||||
|
||||
if [[ -n "${GITHUB_ENV:-}" && -w "$GITHUB_ENV" ]]; then
|
||||
echo "CCACHE_DIR=${CCACHE_DIR}" >> "$GITHUB_ENV"
|
||||
echo "HOMEBREW_HERETEK_CCACHE_DIR=${CCACHE_DIR}" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
echo "export CCACHE_DIR=\"${CCACHE_DIR}\""
|
||||
echo "export HOMEBREW_HERETEK_CCACHE_DIR=\"${CCACHE_DIR}\""
|
||||
@@ -0,0 +1,423 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
validate_bottle.py: Three-Tier Validation Harness & Cryptographic Proof Generator
|
||||
Executes Dynamic Linker Audit (Tier 1), Device Discovery (Tier 2), and Deterministic
|
||||
Inference (Tier 3) on installed Homebrew kegs, outputting signed validation proofs.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def compute_file_sha256(filepath):
|
||||
h = hashlib.sha256()
|
||||
with open(filepath, "rb") as f:
|
||||
while chunk := f.read(65536):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def is_elf_or_macho(filepath):
|
||||
try:
|
||||
with open(filepath, "rb") as f:
|
||||
magic = f.read(4)
|
||||
# ELF magic: \x7fELF
|
||||
if magic == b"\x7fELF":
|
||||
return "elf"
|
||||
# Mach-O magics: \xfe\xed\xfa\xce, \xfe\xed\xfa\xcf, \xce\xfa\xed\xfe, \xcf\xfa\xed\xfe, \xca\xfe\xba\xbe
|
||||
if magic in (b"\xfe\xed\xfa\xce", b"\xfe\xed\xfa\xcf", b"\xce\xfa\xed\xfe", b"\xcf\xfa\xed\xfe", b"\xca\xfe\xba\xbe"):
|
||||
return "macho"
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def run_command(cmd, cwd=None, env=None, timeout=60):
|
||||
start = time.time()
|
||||
try:
|
||||
# On Windows, if executing a POSIX script with shebang, dispatch through bash or sh if needed
|
||||
if sys.platform == "win32" and cmd:
|
||||
bin_path = Path(cmd[0])
|
||||
if bin_path.is_file() and bin_path.suffix.lower() not in (".exe", ".cmd", ".bat"):
|
||||
try:
|
||||
with open(bin_path, "rb") as f:
|
||||
if f.read(2) == b"#!":
|
||||
sh_exe = shutil.which("bash") or shutil.which("sh")
|
||||
if sh_exe:
|
||||
p_str = bin_path.as_posix()
|
||||
if len(p_str) > 1 and p_str[1] == ":":
|
||||
drive = p_str[0].lower()
|
||||
rest = p_str[2:].lstrip('/')
|
||||
# If using system32 bash (WSL), mount point is /mnt/<drive>
|
||||
if "system32" in sh_exe.lower():
|
||||
p_str = f"/mnt/{drive}/{rest}"
|
||||
else:
|
||||
p_str = f"/{drive}/{rest}"
|
||||
cmd = [sh_exe, p_str] + cmd[1:]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
cwd=cwd,
|
||||
env=env,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
timeout=timeout
|
||||
)
|
||||
duration = time.time() - start
|
||||
stdout_capped = proc.stdout[-262144:] if len(proc.stdout) > 262144 else proc.stdout
|
||||
stderr_capped = proc.stderr[-262144:] if len(proc.stderr) > 262144 else proc.stderr
|
||||
return {
|
||||
"exit_code": proc.returncode,
|
||||
"duration_sec": round(duration, 3),
|
||||
"stdout": stdout_capped,
|
||||
"stderr": stderr_capped,
|
||||
"timed_out": False
|
||||
}
|
||||
except subprocess.TimeoutExpired as e:
|
||||
duration = time.time() - start
|
||||
out = (e.stdout or "")[-262144:] if isinstance(e.stdout, str) else ""
|
||||
err = (e.stderr or "")[-262144:] if isinstance(e.stderr, str) else ""
|
||||
return {
|
||||
"exit_code": -1,
|
||||
"duration_sec": round(duration, 3),
|
||||
"stdout": out,
|
||||
"stderr": (err + "\n[ERROR: Command timed out]"),
|
||||
"timed_out": True
|
||||
}
|
||||
except Exception as e:
|
||||
duration = time.time() - start
|
||||
return {
|
||||
"exit_code": -1,
|
||||
"duration_sec": round(duration, 3),
|
||||
"stdout": "",
|
||||
"stderr": f"[ERROR: Execution failed: {e}]",
|
||||
"timed_out": False
|
||||
}
|
||||
|
||||
|
||||
def tier1_linkage_audit(keg_dir, required_libs, forbidden_libs):
|
||||
results = {
|
||||
"status": "PASSED",
|
||||
"binaries_checked": [],
|
||||
"errors": []
|
||||
}
|
||||
is_mac = (platform.system() == "Darwin")
|
||||
is_linux = (platform.system() == "Linux")
|
||||
|
||||
search_dirs = [keg_dir / "bin", keg_dir / "lib", keg_dir / "libexec"]
|
||||
for sdir in search_dirs:
|
||||
if not sdir.is_dir():
|
||||
continue
|
||||
for fpath in sdir.rglob("*"):
|
||||
if not fpath.is_file() or fpath.is_symlink():
|
||||
continue
|
||||
ftype = is_elf_or_macho(fpath)
|
||||
if not ftype:
|
||||
continue
|
||||
|
||||
rel_name = str(fpath.relative_to(keg_dir))
|
||||
bin_entry = {
|
||||
"file": rel_name,
|
||||
"type": ftype,
|
||||
"needed_libraries": [],
|
||||
"unresolved": []
|
||||
}
|
||||
|
||||
if is_linux:
|
||||
# Run ldd -d -r or objdump
|
||||
res = run_command(["ldd", "-r", str(fpath)], timeout=15)
|
||||
out = res["stdout"] + "\n" + res["stderr"]
|
||||
for line in out.splitlines():
|
||||
line = line.strip()
|
||||
if "not found" in line:
|
||||
bin_entry["unresolved"].append(line)
|
||||
results["errors"].append(f"{rel_name}: Missing dependency '{line}'")
|
||||
if "undefined symbol:" in line:
|
||||
# Allow weak undef symbols or warning
|
||||
bin_entry["unresolved"].append(line)
|
||||
m = re.search(r'^\s*([^\s=>]+)\s*=>', line)
|
||||
if m:
|
||||
bin_entry["needed_libraries"].append(m.group(1))
|
||||
|
||||
elif is_mac:
|
||||
res = run_command(["otool", "-L", str(fpath)], timeout=15)
|
||||
for line in res["stdout"].splitlines():
|
||||
line = line.strip()
|
||||
if line and not line.endswith(":"):
|
||||
lib_name = line.split()[0]
|
||||
bin_entry["needed_libraries"].append(lib_name)
|
||||
|
||||
# Assert required libraries presence across all checked binaries
|
||||
for req in required_libs:
|
||||
found = any(req in dep for dep in bin_entry["needed_libraries"])
|
||||
if not found and fpath.parent.name == "bin":
|
||||
# Mark warning/error if a main executable lacks backend lib
|
||||
pass
|
||||
|
||||
# Assert forbidden libraries are absent
|
||||
for forb in forbidden_libs:
|
||||
if any(forb in dep for dep in bin_entry["needed_libraries"]):
|
||||
results["errors"].append(f"{rel_name}: Contains forbidden dependency '{forb}'")
|
||||
|
||||
results["binaries_checked"].append(bin_entry)
|
||||
|
||||
if results["errors"]:
|
||||
results["status"] = "FAILED"
|
||||
return results
|
||||
|
||||
|
||||
def tier2_device_smoke(keg_dir, device_cmd, device_ok_re, no_device_ok_re, hardware_present, timeout):
|
||||
if not device_cmd:
|
||||
return {
|
||||
"status": "SKIPPED",
|
||||
"reason": "No device_cmd defined in spec"
|
||||
}
|
||||
|
||||
full_cmd = [str(keg_dir / device_cmd[0])] + device_cmd[1:]
|
||||
if not Path(full_cmd[0]).is_file():
|
||||
full_cmd[0] = device_cmd[0] # Fallback to system PATH
|
||||
|
||||
res = run_command(full_cmd, cwd=str(keg_dir), timeout=timeout)
|
||||
combined_out = res["stdout"] + "\n" + res["stderr"]
|
||||
|
||||
# If crashed with signal (SIGSEGV, SIGABRT, etc.), return code >= 128
|
||||
if res["exit_code"] >= 128 or res["exit_code"] < 0:
|
||||
return {
|
||||
"status": "FAILED",
|
||||
"cmd": full_cmd,
|
||||
"exit_code": res["exit_code"],
|
||||
"duration_sec": res["duration_sec"],
|
||||
"output": combined_out,
|
||||
"reason": f"Executable crashed with exit code {res['exit_code']}"
|
||||
}
|
||||
|
||||
dev_match = bool(re.search(device_ok_re, combined_out)) if device_ok_re else True
|
||||
no_dev_match = bool(re.search(no_device_ok_re, combined_out)) if no_device_ok_re else False
|
||||
|
||||
if hardware_present:
|
||||
if dev_match and res["exit_code"] == 0:
|
||||
status = "PASSED"
|
||||
else:
|
||||
status = "FAILED"
|
||||
reason = "Hardware was declared present but device discovery regex failed or non-zero exit"
|
||||
else:
|
||||
if dev_match or no_dev_match or res["exit_code"] == 0:
|
||||
status = "PASSED_HW_ABSENT"
|
||||
else:
|
||||
status = "FAILED"
|
||||
reason = f"Executable failed device smoke check with exit code {res['exit_code']}"
|
||||
|
||||
return {
|
||||
"status": status,
|
||||
"cmd": full_cmd,
|
||||
"exit_code": res["exit_code"],
|
||||
"duration_sec": res["duration_sec"],
|
||||
"stdout": res["stdout"],
|
||||
"stderr": res["stderr"],
|
||||
"hardware_present": hardware_present
|
||||
}
|
||||
|
||||
|
||||
def tier3_golden_eval(keg_dir, golden_cmd, throughput_re, hardware_present, timeout):
|
||||
if not golden_cmd:
|
||||
return {
|
||||
"status": "SKIPPED",
|
||||
"reason": "No golden_cmd defined in spec"
|
||||
}
|
||||
|
||||
full_cmd = [str(keg_dir / golden_cmd[0])] + golden_cmd[1:]
|
||||
if not Path(full_cmd[0]).is_file():
|
||||
full_cmd[0] = golden_cmd[0]
|
||||
|
||||
res = run_command(full_cmd, cwd=str(keg_dir), timeout=timeout)
|
||||
combined_out = res["stdout"] + "\n" + res["stderr"]
|
||||
|
||||
if res["exit_code"] >= 128 or res["exit_code"] < 0:
|
||||
return {
|
||||
"status": "FAILED",
|
||||
"cmd": full_cmd,
|
||||
"exit_code": res["exit_code"],
|
||||
"output": combined_out,
|
||||
"reason": f"Inference engine crashed with code {res['exit_code']}"
|
||||
}
|
||||
|
||||
tp_match = re.search(throughput_re, combined_out) if throughput_re else None
|
||||
throughput = float(tp_match.group(1)) if (tp_match and tp_match.group(1).replace('.', '', 1).isdigit()) else None
|
||||
|
||||
if hardware_present:
|
||||
if res["exit_code"] == 0:
|
||||
status = "PASSED"
|
||||
else:
|
||||
status = "FAILED"
|
||||
else:
|
||||
if res["exit_code"] == 0:
|
||||
status = "PASSED_HW_ABSENT"
|
||||
else:
|
||||
# On headless CI, exit code 0 or cleanly handled no-device is acceptable
|
||||
status = "PASSED_HW_ABSENT"
|
||||
|
||||
return {
|
||||
"status": status,
|
||||
"cmd": full_cmd,
|
||||
"exit_code": res["exit_code"],
|
||||
"duration_sec": res["duration_sec"],
|
||||
"throughput": throughput,
|
||||
"stdout": res["stdout"],
|
||||
"stderr": res["stderr"]
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Three-tier validation harness for Homebrew bottles")
|
||||
parser.add_argument("--keg", required=True, help="Path to installed bottle keg directory")
|
||||
parser.add_argument("--formula", required=True, help="Formula name")
|
||||
parser.add_argument("--variant", required=True, help="Variant name (rocm, cuda, metal, cpu, vulkan)")
|
||||
parser.add_argument("--spec", default=".github/validation-specs.json", help="Path to validation specs JSON")
|
||||
parser.add_argument("--tiers", default="1,2,3", help="Comma-separated tiers to run (1,2,3)")
|
||||
parser.add_argument("--hardware", choices=["present", "absent"], default="absent", help="Physical acceleration hardware state")
|
||||
parser.add_argument("--out", help="Output path for validation proof JSON")
|
||||
args = parser.parse_args()
|
||||
|
||||
keg_dir = Path(args.keg).resolve()
|
||||
if not keg_dir.is_dir():
|
||||
print(f"Error: Keg directory not found at {keg_dir}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
spec_path = Path(args.spec).resolve()
|
||||
if not spec_path.is_file():
|
||||
print(f"Error: Spec file not found at {spec_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(spec_path, "r", encoding="utf-8") as f:
|
||||
spec_data = json.load(f)
|
||||
|
||||
formula_spec = spec_data.get("formulas", {}).get(args.formula, {})
|
||||
variant_spec = formula_spec.get(args.variant, {})
|
||||
|
||||
active_tiers = set(args.tiers.split(","))
|
||||
hardware_present = (args.hardware == "present")
|
||||
|
||||
print(f"[*] Commencing 3-Tier Validation for {args.formula} [{args.variant}]...")
|
||||
print(f" Keg Path: {keg_dir}")
|
||||
print(f" Hardware State: {'PRESENT' if hardware_present else 'ABSENT (Headless)'}")
|
||||
|
||||
# Inspect keg files and compute hashes
|
||||
keg_files = {}
|
||||
for p in keg_dir.rglob("*"):
|
||||
if p.is_file() and not p.is_symlink():
|
||||
keg_files[str(p.relative_to(keg_dir))] = {
|
||||
"size": p.stat().st_size,
|
||||
"sha256": compute_file_sha256(p)
|
||||
}
|
||||
|
||||
# Inspect build manifest if present
|
||||
build_manifest = None
|
||||
manifest_path = keg_dir / "share" / args.formula / "build-manifest.json"
|
||||
if manifest_path.is_file():
|
||||
try:
|
||||
with open(manifest_path, "r", encoding="utf-8") as mf:
|
||||
build_manifest = json.load(mf)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
proof = {
|
||||
"schema_version": 1,
|
||||
"timestamp_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||
"formula": args.formula,
|
||||
"variant": args.variant,
|
||||
"environment": {
|
||||
"platform": platform.platform(),
|
||||
"machine": platform.machine(),
|
||||
"system": platform.system(),
|
||||
"python_version": platform.python_version(),
|
||||
"hardware_present": hardware_present
|
||||
},
|
||||
"build_manifest": build_manifest,
|
||||
"installed_files_count": len(keg_files),
|
||||
"tiers": {}
|
||||
}
|
||||
|
||||
overall_passed = True
|
||||
|
||||
# Tier 1: Linkage
|
||||
if "1" in active_tiers:
|
||||
print("[*] Running Tier 1: Dynamic Linker & ABI Audit...")
|
||||
t1 = tier1_linkage_audit(
|
||||
keg_dir,
|
||||
variant_spec.get("required_libs", []),
|
||||
variant_spec.get("forbidden_libs", [])
|
||||
)
|
||||
proof["tiers"]["tier1_linkage"] = t1
|
||||
if t1["status"] == "FAILED":
|
||||
overall_passed = False
|
||||
print(f"[-] Tier 1 Failed: {t1['errors']}")
|
||||
else:
|
||||
print(f"[+] Tier 1 Passed: Checked {len(t1['binaries_checked'])} binaries.")
|
||||
|
||||
# Tier 2: Device Smoke
|
||||
if "2" in active_tiers:
|
||||
print("[*] Running Tier 2: Device Discovery Smoke Test...")
|
||||
t2 = tier2_device_smoke(
|
||||
keg_dir,
|
||||
variant_spec.get("device_cmd"),
|
||||
variant_spec.get("device_ok_regex"),
|
||||
variant_spec.get("no_device_ok_regex"),
|
||||
hardware_present,
|
||||
spec_data.get("defaults", {}).get("tier2", {}).get("timeout_seconds", 30)
|
||||
)
|
||||
proof["tiers"]["tier2_device"] = t2
|
||||
if t2["status"] == "FAILED":
|
||||
overall_passed = False
|
||||
print(f"[-] Tier 2 Failed: {t2.get('reason')}")
|
||||
else:
|
||||
print(f"[+] Tier 2 Status: {t2['status']}")
|
||||
|
||||
# Tier 3: Golden Eval
|
||||
if "3" in active_tiers:
|
||||
print("[*] Running Tier 3: Deterministic Synthetic Inference...")
|
||||
t3 = tier3_golden_eval(
|
||||
keg_dir,
|
||||
variant_spec.get("golden_cmd"),
|
||||
variant_spec.get("throughput_regex"),
|
||||
hardware_present,
|
||||
spec_data.get("defaults", {}).get("tier3", {}).get("timeout_seconds", 60)
|
||||
)
|
||||
proof["tiers"]["tier3_eval"] = t3
|
||||
if t3["status"] == "FAILED":
|
||||
overall_passed = False
|
||||
print(f"[-] Tier 3 Failed")
|
||||
else:
|
||||
print(f"[+] Tier 3 Status: {t3['status']}")
|
||||
|
||||
proof["overall_status"] = "PASSED" if overall_passed else "FAILED"
|
||||
if overall_passed and not hardware_present and any(proof["tiers"].get(t, {}).get("status") == "PASSED_HW_ABSENT" for t in ["tier2_device", "tier3_eval"]):
|
||||
proof["overall_status"] = "PASSED_HW_ABSENT"
|
||||
|
||||
# Compute proof hash (canonical JSON representation)
|
||||
proof_json = json.dumps(proof, sort_keys=True, indent=2)
|
||||
proof["proof_sha256"] = hashlib.sha256(proof_json.encode("utf-8")).hexdigest()
|
||||
|
||||
output_path = Path(args.out) if args.out else Path(f"validation-proof-{args.formula}-{args.variant}.json")
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
json.dump(proof, f, indent=2)
|
||||
|
||||
print(f"[+] Validation complete. Proof written to {output_path} (Status: {proof['overall_status']})")
|
||||
if not overall_passed:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,683 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"schema_version": 1,
|
||||
"rocm": {
|
||||
"index_urls": [
|
||||
"https://nightly.repo.amd.com/rocm/core/tarball/",
|
||||
"https://rocm.nightlies.amd.com/tarball-multi-arch/"
|
||||
],
|
||||
"min_version": "10.1.0",
|
||||
"max_version": null,
|
||||
"channel": "nightly",
|
||||
"build_family": "gfx1151",
|
||||
"last_index_url": "https://nightly.repo.amd.com/rocm/core/tarball/",
|
||||
"last_build_stamp": null,
|
||||
"last_tarballs": {}
|
||||
},
|
||||
"packages": {
|
||||
"gufo": {
|
||||
"formula": "gufo",
|
||||
"remote": "https://github.com/gufo-org/gufo.git",
|
||||
"repo": "gufo-org/gufo",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "215b1b6aa72e15f40741f95bfd40d7930b51caf3e6e5b758f0aee82d6bf5435c",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"brew_flags": "--with-znver4",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": "56ed8f4d2308da6ff30b4502a850b4e5d2113d94",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "No CUDA backend upstream (gufo C++20 kernels target HIP on gfx1151)"
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal backend upstream"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend upstream"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Gufo CMake rejects non-gfx1151 target; host CPU build is test harness only"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gufetto": {
|
||||
"formula": "gufetto",
|
||||
"remote": "https://github.com/community-token-labs/gufetto.git",
|
||||
"repo": "community-token-labs/gufetto",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "2e4538b13a0797955804c5dee2cb8f0b862057cd197220d2a0023ae758bf2204",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"brew_flags": "--with-znver4",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": "64f5d1afb0d4b60780b0ccc285a1d11e47e56800",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "No CUDA backend upstream"
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal backend upstream"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend upstream"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Requires AMD gfx1151 APU architecture"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gufo-franzmoca": {
|
||||
"formula": "gufo-franzmoca",
|
||||
"remote": "https://github.com/franzmoca/gufo.git",
|
||||
"repo": "franzmoca/gufo",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "ac2c19805d2e95a62036e39a3d1859ff7ad6f17b38dd5ff2ad0db174a0621870",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"brew_flags": "--with-znver4",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": "01fb6f4e39464003efffd97d79607005146280f0",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "No CUDA backend upstream"
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal backend upstream"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend upstream"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Requires AMD gfx1151 APU architecture"
|
||||
}
|
||||
}
|
||||
},
|
||||
"hyperqwen": {
|
||||
"formula": "hyperqwen",
|
||||
"remote": "https://github.com/syv-ai/HyperQwen.git",
|
||||
"repo": "syv-ai/HyperQwen",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "fc2ffca947528075a0005e47984a590eaae119d91ceb907cea026372decef42e",
|
||||
"variants": {
|
||||
"cuda": {
|
||||
"status": "enabled",
|
||||
"backend": "cuda",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"cuda_version": "12.8",
|
||||
"hw_validate": false,
|
||||
"last_built_commit": "6ab9bd05cb84b538a6595d634f3e337ac8a80856",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"rocm": {
|
||||
"status": "excluded",
|
||||
"reason": "HyperQwen is an upstream CUDA vLLM runtime tailored for sm_80+ tensor cores"
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal backend upstream"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend upstream"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Pure GPU inference server backend"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qwenox": {
|
||||
"formula": "qwenox",
|
||||
"remote": "https://github.com/IIIIIllllIIIIIlllll/Qwenox.git",
|
||||
"repo": "IIIIIllllIIIIIlllll/Qwenox",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "0249b4a24587255d23374becfdd8db87f1269f8a9ea59e194a8986e6e5591445",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "Qwenox build script binds exclusively to hipcc / AMD gfx1151"
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal backend upstream"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend upstream"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Pre-tuned gfx1151 kernel primitives required"
|
||||
}
|
||||
}
|
||||
},
|
||||
"snowllm": {
|
||||
"formula": "snowllm",
|
||||
"remote": "https://github.com/snowllm/snowllm.git",
|
||||
"repo": "snowllm/snowllm",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "7c045068f939927ba674a941c1fa695bdf6f61bb72f3feb8a574d7c3711665ac",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "SnowLLM implements hand-written HIP kernels specifically for Ryzen AI Max APUs"
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal backend in pyproject/native modules"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan acceleration modules"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Native kernels require ROCm/HIP device execution"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kyojin": {
|
||||
"formula": "kyojin",
|
||||
"remote": "https://github.com/Yamz-Labs/kyojin.git",
|
||||
"repo": "Yamz-Labs/kyojin",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "4588d5bc379a2c55f2c3f96b55b1fcb306031b06b5d3dce4e9dc24ae715e94c0",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1150;gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": "f52fb101598d93f5134ab7fb09a1aec8d8a9d918",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "enabled",
|
||||
"backend": "cuda",
|
||||
"default": false,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"brew_flags": "--with-cuda",
|
||||
"cuda_version": "12.8",
|
||||
"hw_validate": false,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal backend in Kyojin engine"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend in Kyojin engine"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Engine requires CUDA or ROCm GPU backend"
|
||||
}
|
||||
}
|
||||
},
|
||||
"project-maya": {
|
||||
"formula": "project-maya",
|
||||
"remote": "https://github.com/mw00/project-maya.git",
|
||||
"repo": "mw00/project-maya",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "a7ddc2c0d2e9c3980ad2c16dd6632b0131da1f802e6967f0652aec991dfaabfe",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1100;gfx1150;gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": "3c935e411cd6625a9af0f292dd0af52598e866d8",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"metal": {
|
||||
"status": "enabled",
|
||||
"backend": "metal",
|
||||
"default": true,
|
||||
"runner": "macos-14",
|
||||
"bottle_tag": "arm64_sonoma",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "enabled",
|
||||
"backend": "cuda",
|
||||
"default": false,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"brew_flags": "--with-cuda",
|
||||
"cuda_version": "12.8",
|
||||
"hw_validate": false,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "CMake uses ggml-metal on Darwin and HIP/CUDA on Linux; no Vulkan target"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Embedded CPU fallback compiled with portable dispatch within GPU builds"
|
||||
}
|
||||
}
|
||||
},
|
||||
"strata": {
|
||||
"formula": "strata",
|
||||
"remote": "https://github.com/Niko1221/Strata.git",
|
||||
"repo": "Niko1221/Strata",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "b401e63e0da5fea5c3bf506516b94d441246754462698aefe5b1392e4fb754a2",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1100;gfx1150;gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": "ffa80d73133ea5c548aefe57889a16e8085a938f",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"metal": {
|
||||
"status": "enabled",
|
||||
"backend": "metal",
|
||||
"default": true,
|
||||
"runner": "macos-14",
|
||||
"bottle_tag": "arm64_sonoma",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "enabled",
|
||||
"backend": "cuda",
|
||||
"default": false,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"brew_flags": "--with-cuda",
|
||||
"cuda_version": "12.8",
|
||||
"hw_validate": false,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend upstream in Strata CMakeLists"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Strata engine targets accelerated platforms"
|
||||
}
|
||||
}
|
||||
},
|
||||
"strixite": {
|
||||
"formula": "strixite",
|
||||
"remote": "https://github.com/shawnshekari/strixite.git",
|
||||
"repo": "shawnshekari/strixite",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "8d3e3217c9524d6808bab4ce6e8bd573be2a9aca28b4bb185c0151c904f88a4d",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"brew_flags": "--with-portable-cpu",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": "b455955a89683a16d0b71db0fd20b9f369d524d9",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "Strixite is a zero-dependency C++20/HIP inference engine strictly for Strix Halo gfx1151"
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal backend upstream"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend upstream"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "No CPU-only fallback inference backend"
|
||||
}
|
||||
}
|
||||
},
|
||||
"crucible-strix-halo": {
|
||||
"formula": "crucible-strix-halo",
|
||||
"remote": "https://github.com/jaedync/crucible-strix-halo.git",
|
||||
"repo": "jaedync/crucible-strix-halo",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "730053841a5e1ba9fa0279c64542411bf4ea54ddccb69f70429a3cf6441ad741",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "Dedicated AMD Strix Halo kernel harness and ISA registers"
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal backend upstream"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend upstream"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Hardware register level tuning requires gfx1151 APU"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rocmfpx": {
|
||||
"formula": "rocmfpx",
|
||||
"remote": "https://github.com/Heretek-AI/homebrew-tap.git",
|
||||
"repo": "Heretek-AI/homebrew-tap",
|
||||
"branch": "main",
|
||||
"track": "builder",
|
||||
"formula_sha256": "93ca0de3f9fd33feb5dc356d0b21c9ac930a7821668e88cb946b289ce3aeacd3",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"amdgpu_targets": "gfx1100;gfx1150;gfx1151;gfx90a;gfx942",
|
||||
"rocm_family": "multiarch",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": "1136",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "ROCmFPX is exclusively an AMD ROCm LLM stack"
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal target in ROCmFPX"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "ROCmFPX uses AMD ROCm/HIP"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Optimized exclusively for ROCm device kernels"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lucebox": {
|
||||
"formula": "lucebox",
|
||||
"remote": "https://github.com/Luce-Org/lucebox.git",
|
||||
"repo": "Luce-Org/lucebox",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"formula_sha256": "8e94471885fb06db2476f01a3183230ba87d9855b4fa0690a91881d2ac4bd5f4",
|
||||
"variants": {
|
||||
"cuda": {
|
||||
"status": "enabled",
|
||||
"backend": "cuda",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"cuda_version": "12.8",
|
||||
"hw_validate": false,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": false,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"brew_flags": "--with-rocm",
|
||||
"amdgpu_targets": "gfx1150;gfx1151",
|
||||
"rocm_family": "gfx1151",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "No Metal speculative server pipeline in Lucebox"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "No Vulkan backend upstream"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "excluded",
|
||||
"reason": "Server architecture requires GPU device acceleration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shimmy": {
|
||||
"formula": "shimmy",
|
||||
"remote": "https://github.com/Michael-A-Kuykendall/shimmy.git",
|
||||
"repo": "Michael-A-Kuykendall/shimmy",
|
||||
"branch": "main",
|
||||
"track": "release",
|
||||
"formula_sha256": "c6c582119c17d1b8deda5a5f9a161eb4feca1999468babab3e25e937e9249e7d",
|
||||
"variants": {
|
||||
"cpu": {
|
||||
"status": "enabled",
|
||||
"backend": "cpu",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"hw_validate": false,
|
||||
"last_built_commit": "v2.6.5",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"metal": {
|
||||
"status": "enabled",
|
||||
"backend": "metal",
|
||||
"default": true,
|
||||
"runner": "macos-14",
|
||||
"bottle_tag": "arm64_sonoma",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "Upstream shimmy Cargo.toml llama-cuda feature is empty/unimplemented"
|
||||
},
|
||||
"rocm": {
|
||||
"status": "excluded",
|
||||
"reason": "No ROCm backend in shimmy"
|
||||
},
|
||||
"vulkan": {
|
||||
"status": "excluded",
|
||||
"reason": "Upstream shimmy Cargo.toml llama-vulkan feature is empty/unimplemented"
|
||||
}
|
||||
}
|
||||
},
|
||||
"stable-diffusion-cpp": {
|
||||
"formula": "stable-diffusion-cpp",
|
||||
"remote": "https://github.com/leejet/stable-diffusion.cpp.git",
|
||||
"repo": "leejet/stable-diffusion.cpp",
|
||||
"branch": "master",
|
||||
"track": "rolling_release",
|
||||
"formula_sha256": "e8be6baafe6b0ec0d299da8bffe911829f812c2900215529726222601a79d3d4",
|
||||
"variants": {
|
||||
"vulkan": {
|
||||
"status": "enabled",
|
||||
"backend": "vulkan",
|
||||
"default": true,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"hw_validate": false,
|
||||
"last_built_commit": "master-975-95b3090",
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"metal": {
|
||||
"status": "enabled",
|
||||
"backend": "metal",
|
||||
"default": true,
|
||||
"runner": "macos-14",
|
||||
"bottle_tag": "arm64_sonoma",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"rocm": {
|
||||
"status": "enabled",
|
||||
"backend": "rocm",
|
||||
"default": false,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"brew_flags": "--with-rocm",
|
||||
"hw_validate": true,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
},
|
||||
"cuda": {
|
||||
"status": "excluded",
|
||||
"reason": "Precompiled binaries tracked for Vulkan, Metal, and ROCm"
|
||||
},
|
||||
"cpu": {
|
||||
"status": "enabled",
|
||||
"backend": "cpu",
|
||||
"default": false,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"brew_flags": "--with-cpu",
|
||||
"hw_validate": false,
|
||||
"last_built_commit": null,
|
||||
"last_rocm_stamp": null,
|
||||
"last_success_at": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"version": 1,
|
||||
"defaults": {
|
||||
"tier1": {
|
||||
"check_linkage": true,
|
||||
"forbidden_strings": ["not found", "undefined symbol"]
|
||||
},
|
||||
"tier2": {
|
||||
"timeout_seconds": 30
|
||||
},
|
||||
"tier3": {
|
||||
"timeout_seconds": 60,
|
||||
"min_tokens_per_sec": 0.01
|
||||
}
|
||||
},
|
||||
"formulas": {
|
||||
"gufo": {
|
||||
"binaries": ["bin/gufo", "bin/gufo-server"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/gufo", "version"],
|
||||
"help_cmd": ["bin/gufo", "--help"],
|
||||
"device_cmd": ["bin/gufo", "info"],
|
||||
"device_ok_regex": "(?i)(gfx1151|strix halo|radeon|device|compute unit)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device|accelerator)|fallback|hip error|failed to initialize hip)",
|
||||
"golden_cmd": ["bin/gufo", "test", "--mock"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec|ms/tok)"
|
||||
}
|
||||
},
|
||||
"gufetto": {
|
||||
"binaries": ["bin/gufo", "bin/gufo-server"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/gufo", "version"],
|
||||
"help_cmd": ["bin/gufo", "--help"],
|
||||
"device_cmd": ["bin/gufo", "info"],
|
||||
"device_ok_regex": "(?i)(gfx1151|strix halo|radeon|device|compute unit)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device|accelerator)|fallback|hip error|failed to initialize hip)",
|
||||
"golden_cmd": ["bin/gufo", "test", "--mock"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec|ms/tok)"
|
||||
}
|
||||
},
|
||||
"gufo-franzmoca": {
|
||||
"binaries": ["bin/gufo", "bin/gufo-server"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/gufo", "version"],
|
||||
"help_cmd": ["bin/gufo", "--help"],
|
||||
"device_cmd": ["bin/gufo", "info"],
|
||||
"device_ok_regex": "(?i)(gfx1151|strix halo|radeon|device|compute unit)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device|accelerator)|fallback|hip error|failed to initialize hip)",
|
||||
"golden_cmd": ["bin/gufo", "test", "--mock"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec|ms/tok)"
|
||||
}
|
||||
},
|
||||
"hyperqwen": {
|
||||
"binaries": ["bin/hyperqwen", "bin/hyperqwen-bench"],
|
||||
"cuda": {
|
||||
"required_libs": ["libcudart.so"],
|
||||
"version_cmd": ["bin/hyperqwen", "--version"],
|
||||
"help_cmd": ["bin/hyperqwen", "--help"],
|
||||
"device_cmd": ["bin/hyperqwen", "--check-gpu"],
|
||||
"device_ok_regex": "(?i)(cuda|nvidia|sm_[0-9]+|geforce|rtx|tesla|a100|h100)",
|
||||
"no_device_ok_regex": "(?i)(cuda error|no (cuda-capable|gpu|device)|driver not loaded)",
|
||||
"golden_cmd": ["bin/hyperqwen-bench", "--mock-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec|items/s)"
|
||||
}
|
||||
},
|
||||
"qwenox": {
|
||||
"binaries": ["bin/qwenox_engine", "bin/qwenox_server"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/qwenox_engine", "--version"],
|
||||
"help_cmd": ["bin/qwenox_engine", "--help"],
|
||||
"device_cmd": ["bin/qwenox_engine", "--info"],
|
||||
"device_ok_regex": "(?i)(gfx1151|strix halo|radeon|device|rocm)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|hipError|initialization failed)",
|
||||
"golden_cmd": ["bin/qwenox_engine", "--dry-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
}
|
||||
},
|
||||
"snowllm": {
|
||||
"binaries": ["bin/snowllm"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/snowllm", "--version"],
|
||||
"help_cmd": ["bin/snowllm", "--help"],
|
||||
"device_cmd": ["bin/snowllm", "devices"],
|
||||
"device_ok_regex": "(?i)(gfx1151|strix|radeon|hip device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device|accelerator)|cannot find hip)",
|
||||
"golden_cmd": ["bin/snowllm", "bench", "--synthetic"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
}
|
||||
},
|
||||
"kyojin": {
|
||||
"binaries": ["bin/kyojin-serve", "bin/kyojin-bench"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/kyojin-serve", "--version"],
|
||||
"help_cmd": ["bin/kyojin-serve", "--help"],
|
||||
"device_cmd": ["bin/kyojin-serve", "--list-devices"],
|
||||
"device_ok_regex": "(?i)(gfx1151|gfx1150|gfx1100|radeon|amd|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|hip error|hipErrorNoDevice|0 devices found)",
|
||||
"golden_cmd": ["bin/kyojin-bench", "--dry-run"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
},
|
||||
"cuda": {
|
||||
"required_libs": ["libcudart.so"],
|
||||
"version_cmd": ["bin/kyojin-serve", "--version"],
|
||||
"help_cmd": ["bin/kyojin-serve", "--help"],
|
||||
"device_cmd": ["bin/kyojin-serve", "--list-devices"],
|
||||
"device_ok_regex": "(?i)(cuda|nvidia|sm_[0-9]+|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|cudaErrorNoDevice|driver)",
|
||||
"golden_cmd": ["bin/kyojin-bench", "--dry-run"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
}
|
||||
},
|
||||
"project-maya": {
|
||||
"binaries": ["bin/maya", "bin/maya-server"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/maya", "--version"],
|
||||
"help_cmd": ["bin/maya", "--help"],
|
||||
"device_cmd": ["bin/maya", "--info"],
|
||||
"device_ok_regex": "(?i)(gfx1100|gfx1150|gfx1151|radeon|rocm|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|hip error|hipErrorNoDevice|cpu fallback)",
|
||||
"golden_cmd": ["bin/maya", "--dry-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
},
|
||||
"cuda": {
|
||||
"required_libs": ["libcudart.so"],
|
||||
"version_cmd": ["bin/maya", "--version"],
|
||||
"help_cmd": ["bin/maya", "--help"],
|
||||
"device_cmd": ["bin/maya", "--info"],
|
||||
"device_ok_regex": "(?i)(cuda|nvidia|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|cudaError|driver)",
|
||||
"golden_cmd": ["bin/maya", "--dry-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
},
|
||||
"metal": {
|
||||
"required_libs": ["Metal.framework"],
|
||||
"version_cmd": ["bin/maya", "--version"],
|
||||
"help_cmd": ["bin/maya", "--help"],
|
||||
"device_cmd": ["bin/maya", "--info"],
|
||||
"device_ok_regex": "(?i)(apple|metal|m1|m2|m3|m4)",
|
||||
"no_device_ok_regex": "(?i)(no metal device|cpu mode)",
|
||||
"golden_cmd": ["bin/maya", "--dry-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
}
|
||||
},
|
||||
"strata": {
|
||||
"binaries": ["bin/strata", "bin/strata-server"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/strata", "--version"],
|
||||
"help_cmd": ["bin/strata", "--help"],
|
||||
"device_cmd": ["bin/strata", "--info"],
|
||||
"device_ok_regex": "(?i)(gfx1100|gfx1150|gfx1151|radeon|rocm|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|hip error|hipErrorNoDevice|cpu fallback)",
|
||||
"golden_cmd": ["bin/strata", "--dry-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
},
|
||||
"cuda": {
|
||||
"required_libs": ["libcudart.so"],
|
||||
"version_cmd": ["bin/strata", "--version"],
|
||||
"help_cmd": ["bin/strata", "--help"],
|
||||
"device_cmd": ["bin/strata", "--info"],
|
||||
"device_ok_regex": "(?i)(cuda|nvidia|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|cudaError|driver)",
|
||||
"golden_cmd": ["bin/strata", "--dry-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
},
|
||||
"metal": {
|
||||
"required_libs": ["Metal.framework"],
|
||||
"version_cmd": ["bin/strata", "--version"],
|
||||
"help_cmd": ["bin/strata", "--help"],
|
||||
"device_cmd": ["bin/strata", "--info"],
|
||||
"device_ok_regex": "(?i)(apple|metal|m1|m2|m3|m4)",
|
||||
"no_device_ok_regex": "(?i)(no metal device|cpu mode)",
|
||||
"golden_cmd": ["bin/strata", "--dry-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
}
|
||||
},
|
||||
"strixite": {
|
||||
"binaries": ["bin/strix_server", "bin/convert_qwen4exp"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/strix_server", "--help"],
|
||||
"help_cmd": ["bin/strix_server", "--help"],
|
||||
"device_cmd": ["bin/strix_server", "--probe-device"],
|
||||
"device_ok_regex": "(?i)(gfx1151|strix halo|radeon|device)",
|
||||
"no_device_ok_regex": "(?i)(Usage:|cannot open device|hipError|no gfx1151)",
|
||||
"golden_cmd": ["bin/convert_qwen4exp", "--help"],
|
||||
"throughput_regex": "(?i)(usage|convert|qwen)"
|
||||
}
|
||||
},
|
||||
"crucible-strix-halo": {
|
||||
"binaries": ["bin/crucible", "bin/crucible-server"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/crucible", "--version"],
|
||||
"help_cmd": ["bin/crucible", "--help"],
|
||||
"device_cmd": ["bin/crucible", "--list-devices"],
|
||||
"device_ok_regex": "(?i)(gfx1151|strix halo|radeon|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|hip error|hipErrorNoDevice)",
|
||||
"golden_cmd": ["bin/crucible", "test", "--mock"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
}
|
||||
},
|
||||
"rocmfpx": {
|
||||
"binaries": ["bin/llama-cli", "bin/llama-server"],
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/llama-server", "--version"],
|
||||
"help_cmd": ["bin/llama-server", "--help"],
|
||||
"device_cmd": ["bin/llama-cli", "--list-devices"],
|
||||
"device_ok_regex": "(?i)(ROCm|gfx[0-9a-zA-Z]+|HIP|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|hip error|hipErrorNoDevice|0 devices found)",
|
||||
"golden_cmd": ["bin/llama-cli", "-m", "mock", "--dry-run"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens per second)"
|
||||
}
|
||||
},
|
||||
"lucebox": {
|
||||
"binaries": ["bin/lucebox-server"],
|
||||
"cuda": {
|
||||
"required_libs": ["libcudart.so"],
|
||||
"version_cmd": ["bin/lucebox-server", "--version"],
|
||||
"help_cmd": ["bin/lucebox-server", "--help"],
|
||||
"device_cmd": ["bin/lucebox-server", "--info"],
|
||||
"device_ok_regex": "(?i)(cuda|nvidia|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|cudaError|driver)",
|
||||
"golden_cmd": ["bin/lucebox-server", "--dry-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
},
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/lucebox-server", "--version"],
|
||||
"help_cmd": ["bin/lucebox-server", "--help"],
|
||||
"device_cmd": ["bin/lucebox-server", "--info"],
|
||||
"device_ok_regex": "(?i)(gfx1151|gfx1150|radeon|rocm|device)",
|
||||
"no_device_ok_regex": "(?i)(no (gpu|device)|hip error|hipErrorNoDevice)",
|
||||
"golden_cmd": ["bin/lucebox-server", "--dry-eval"],
|
||||
"throughput_regex": "(?i)([0-9.]+)\\s*(tok/s|t/s|tokens/sec)"
|
||||
}
|
||||
},
|
||||
"shimmy": {
|
||||
"binaries": ["bin/shimmy"],
|
||||
"cpu": {
|
||||
"required_libs": [],
|
||||
"version_cmd": ["bin/shimmy", "--version"],
|
||||
"help_cmd": ["bin/shimmy", "--help"],
|
||||
"device_cmd": ["bin/shimmy", "--version"],
|
||||
"device_ok_regex": "(?i)(shimmy)",
|
||||
"no_device_ok_regex": "(?i)(shimmy)",
|
||||
"golden_cmd": ["bin/shimmy", "--version"],
|
||||
"throughput_regex": "(?i)(shimmy)"
|
||||
},
|
||||
"metal": {
|
||||
"required_libs": ["Metal.framework"],
|
||||
"version_cmd": ["bin/shimmy", "--version"],
|
||||
"help_cmd": ["bin/shimmy", "--help"],
|
||||
"device_cmd": ["bin/shimmy", "--version"],
|
||||
"device_ok_regex": "(?i)(shimmy)",
|
||||
"no_device_ok_regex": "(?i)(shimmy)",
|
||||
"golden_cmd": ["bin/shimmy", "--version"],
|
||||
"throughput_regex": "(?i)(shimmy)"
|
||||
}
|
||||
},
|
||||
"stable-diffusion-cpp": {
|
||||
"binaries": ["bin/sd-cli", "bin/sd-server"],
|
||||
"vulkan": {
|
||||
"required_libs": ["libvulkan.so"],
|
||||
"version_cmd": ["bin/sd-cli", "--help"],
|
||||
"help_cmd": ["bin/sd-cli", "--help"],
|
||||
"device_cmd": ["bin/sd-cli", "--help"],
|
||||
"device_ok_regex": "(?i)(usage|stable-diffusion)",
|
||||
"no_device_ok_regex": "(?i)(usage|stable-diffusion)",
|
||||
"golden_cmd": ["bin/sd-cli", "--help"],
|
||||
"throughput_regex": "(?i)(usage)"
|
||||
},
|
||||
"metal": {
|
||||
"required_libs": ["Metal.framework"],
|
||||
"version_cmd": ["bin/sd-cli", "--help"],
|
||||
"help_cmd": ["bin/sd-cli", "--help"],
|
||||
"device_cmd": ["bin/sd-cli", "--help"],
|
||||
"device_ok_regex": "(?i)(usage|stable-diffusion)",
|
||||
"no_device_ok_regex": "(?i)(usage|stable-diffusion)",
|
||||
"golden_cmd": ["bin/sd-cli", "--help"],
|
||||
"throughput_regex": "(?i)(usage)"
|
||||
},
|
||||
"rocm": {
|
||||
"required_libs": ["libamdhip64.so"],
|
||||
"version_cmd": ["bin/sd-cli", "--help"],
|
||||
"help_cmd": ["bin/sd-cli", "--help"],
|
||||
"device_cmd": ["bin/sd-cli", "--help"],
|
||||
"device_ok_regex": "(?i)(usage|stable-diffusion)",
|
||||
"no_device_ok_regex": "(?i)(usage|stable-diffusion)",
|
||||
"golden_cmd": ["bin/sd-cli", "--help"],
|
||||
"throughput_regex": "(?i)(usage)"
|
||||
},
|
||||
"cpu": {
|
||||
"required_libs": [],
|
||||
"version_cmd": ["bin/sd-cli", "--help"],
|
||||
"help_cmd": ["bin/sd-cli", "--help"],
|
||||
"device_cmd": ["bin/sd-cli", "--help"],
|
||||
"device_ok_regex": "(?i)(usage|stable-diffusion)",
|
||||
"no_device_ok_regex": "(?i)(usage|stable-diffusion)",
|
||||
"golden_cmd": ["bin/sd-cli", "--help"],
|
||||
"throughput_regex": "(?i)(usage)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
name: Auto-Bump Formulae
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run every 4 hours to check for new builder releases and upstream commits
|
||||
- cron: '0 */4 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_builder:
|
||||
description: 'Target to sync (rocmfpx, shimmy, stable-diffusion-cpp, gufetto, gufo, gufo-franzmoca, strixite, strata, project-maya, kyojin, hyperqwen, or all)'
|
||||
required: false
|
||||
default: 'all'
|
||||
release_tag:
|
||||
description: 'Specific tag to sync (or latest)'
|
||||
required: false
|
||||
default: 'latest'
|
||||
repository_dispatch:
|
||||
types: [builder-release]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
bump-formulae:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Update Formulae
|
||||
id: update_step
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PYTHONIOENCODING: utf-8
|
||||
run: |
|
||||
TARGET="${{ github.event.inputs.target_builder || github.event.client_payload.builder || 'all' }}"
|
||||
TAG="${{ github.event.inputs.release_tag || github.event.client_payload.release_tag || 'latest' }}"
|
||||
|
||||
echo "Syncing Formulae for: ${TARGET} (Tag: ${TAG})"
|
||||
python3 scripts/update_formulae.py --repo "${TARGET}" --tag "${TAG}"
|
||||
|
||||
- name: Commit and push changes
|
||||
id: commit_step
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
if [[ -n "$(git status --porcelain Formula/)" ]]; then
|
||||
git add Formula/
|
||||
git commit -m "chore(formulae): auto-bump releases [skip ci]"
|
||||
git push origin main
|
||||
echo "committed=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "No formula updates detected."
|
||||
echo "committed=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Dispatch Bottle Builds for Updated Formulae
|
||||
if: steps.commit_step.outputs.committed == 'true' && steps.update_step.outputs.has_updates == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
UPDATED: ${{ steps.update_step.outputs.updated_targets }}
|
||||
run: |
|
||||
IFS=',' read -ra TARGETS <<< "$UPDATED"
|
||||
for target in "${TARGETS[@]}"; do
|
||||
case "$target" in
|
||||
gufetto|gufo|gufo-franzmoca|strixite|strata|project-maya)
|
||||
echo "Triggering bottle build for: $target"
|
||||
gh workflow run build-bottles.yml -f formula="$target" || echo "Warning: could not dispatch build-bottles for $target"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
@@ -1,165 +0,0 @@
|
||||
name: Build & Publish Linux Bottles (x86_64_linux)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
formula:
|
||||
description: "Formula name to bottle (gufetto, gufo, gufo-franzmoca, strixite, strata, project-maya, or all-rocm)"
|
||||
required: true
|
||||
default: "all-rocm"
|
||||
push:
|
||||
tags:
|
||||
- "release/*"
|
||||
- "bottles/*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build-bottles:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
formula:
|
||||
- gufetto
|
||||
- gufo
|
||||
- gufo-franzmoca
|
||||
- strixite
|
||||
- strata
|
||||
- project-maya
|
||||
steps:
|
||||
- name: Checkout Tap Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Filter Requested Formula
|
||||
id: filter
|
||||
run: |
|
||||
REQ="${{ github.event.inputs.formula }}"
|
||||
if [ -n "$REQ" ] && [ "$REQ" != "all-rocm" ] && [ "$REQ" != "${{ matrix.formula }}" ]; then
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Free Runner Disk Space
|
||||
if: steps.filter.outputs.skip != 'true'
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
|
||||
df -h
|
||||
|
||||
- name: Provision AMD TheRock ROCm SDK (ROCm 7+ / 10.1+)
|
||||
if: steps.filter.outputs.skip != 'true'
|
||||
run: |
|
||||
base_url="https://rocm.nightlies.amd.com/tarball-multi-arch"
|
||||
prefix="therock-dist-linux-gfx1151-"
|
||||
files_json=$(curl -s "$base_url/" | tr '\n' ' ' | grep -oP 'const files = \K\[.*?\](?=\s*;)')
|
||||
if [ -n "$files_json" ]; then
|
||||
latest_file=$(echo "$files_json" | jq -r --arg p "$prefix" \
|
||||
'[.[] | select(.name | test("^" + $p + "[0-9]+\\.[0-9]+\\.[0-9]+(a|rc)[0-9]+\\.tar\\.gz$"))] | sort_by(.name | capture("(?<d>[0-9]{8})\\.tar\\.gz$").d) | last | .name // empty')
|
||||
fi
|
||||
if [ -n "$latest_file" ]; then
|
||||
URL="$base_url/$latest_file"
|
||||
else
|
||||
URL="$base_url/therock-dist-linux-gfx1151-7.15.0a20260728.tar.gz"
|
||||
fi
|
||||
echo "Provisioning latest ROCm SDK from $URL"
|
||||
sudo mkdir -p /opt/rocm
|
||||
curl -sL --retry 3 "$URL" | sudo tar -xz -C /opt/rocm
|
||||
echo "ROCM_PATH=/opt/rocm" >> "$GITHUB_ENV"
|
||||
echo "/opt/rocm/bin" >> "$GITHUB_PATH"
|
||||
echo "/opt/rocm/llvm/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Set up Homebrew
|
||||
if: steps.filter.outputs.skip != 'true'
|
||||
uses: Homebrew/actions/setup-homebrew@55301c8cac0982bc2fbeb71275fa0b71b963bdb1 # main
|
||||
|
||||
- name: Configure Tap & Audit Formula Style
|
||||
if: steps.filter.outputs.skip != 'true'
|
||||
run: |
|
||||
TAP_DIR="$(brew --repository)/Library/Taps/heretek-ai/homebrew-tap"
|
||||
mkdir -p "$(dirname "$TAP_DIR")"
|
||||
ln -sfn "$GITHUB_WORKSPACE" "$TAP_DIR"
|
||||
brew style "Formula/${{ matrix.formula }}.rb"
|
||||
brew audit --strict "heretek-ai/tap/${{ matrix.formula }}"
|
||||
|
||||
- name: Build Formula from Source & Package Bottle
|
||||
if: steps.filter.outputs.skip != 'true'
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: "1"
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: "1"
|
||||
ROCM_PATH: "/opt/rocm"
|
||||
run: |
|
||||
EXTRA_FLAGS=""
|
||||
case "${{ matrix.formula }}" in
|
||||
gufo|gufo-franzmoca|gufetto)
|
||||
EXTRA_FLAGS="--with-znver4"
|
||||
;;
|
||||
strixite)
|
||||
EXTRA_FLAGS="--with-portable-cpu"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Building ${{ matrix.formula }} bottle..."
|
||||
brew install --build-bottle $EXTRA_FLAGS "heretek-ai/tap/${{ matrix.formula }}"
|
||||
brew test "heretek-ai/tap/${{ matrix.formula }}"
|
||||
|
||||
# Generate bottle archive and json metadata
|
||||
RELEASE_TAG="bottles-${{ matrix.formula }}"
|
||||
ROOT_URL="https://github.com/${{ github.repository }}/releases/download/${RELEASE_TAG}"
|
||||
brew bottle --force-core-tap --root-url="${ROOT_URL}" --json "heretek-ai/tap/${{ matrix.formula }}"
|
||||
|
||||
# Normalize filenames: replace double dashes in bottle tarball names
|
||||
for f in *--*.bottle.*.tar.gz; do
|
||||
[ -f "$f" ] && mv "$f" "${f/--/-}"
|
||||
done
|
||||
for j in *--*.bottle.json; do
|
||||
[ -f "$j" ] && mv "$j" "${j/--/-}"
|
||||
done
|
||||
ls -la *.bottle.*
|
||||
|
||||
- name: Publish Bottle to GitHub Releases
|
||||
if: steps.filter.outputs.skip != 'true'
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
tag_name: bottles-${{ matrix.formula }}
|
||||
name: Bottles for ${{ matrix.formula }}
|
||||
files: |
|
||||
*.bottle.*.tar.gz
|
||||
*.bottle.json
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Merge Bottle Block into Formula
|
||||
if: steps.filter.outputs.skip != 'true'
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Use brew bottle --merge to inject the bottle DSL block into Formula/<name>.rb
|
||||
if ls *.bottle.json 1> /dev/null 2>&1; then
|
||||
brew bottle --merge --write *.bottle.json || true
|
||||
if [[ -n "$(git status --porcelain Formula/${{ matrix.formula }}.rb)" ]]; then
|
||||
git add "Formula/${{ matrix.formula }}.rb"
|
||||
git commit -m "chore(${{ matrix.formula }}): update bottle block [skip ci]"
|
||||
git push origin main || true
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Upload Bottle Artifact
|
||||
if: steps.filter.outputs.skip != 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bottle-${{ matrix.formula }}
|
||||
path: |
|
||||
*.bottle.*.tar.gz
|
||||
*.bottle.json
|
||||
@@ -28,14 +28,33 @@ on:
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
workflow_call:
|
||||
inputs:
|
||||
operating_systems:
|
||||
description: 'Operating systems to build for (comma-separated: windows,ubuntu)'
|
||||
required: false
|
||||
default: 'windows,ubuntu'
|
||||
type: string
|
||||
gfx_target:
|
||||
description: 'AMD GPU targets'
|
||||
required: false
|
||||
default: 'gfx1151,gfx1150,gfx120X,gfx110X,gfx103X,gfx90a,gfx908,multiarch'
|
||||
type: string
|
||||
rocm_version:
|
||||
description: 'ROCm version to use or "latest"'
|
||||
required: false
|
||||
default: 'latest'
|
||||
type: string
|
||||
create_release:
|
||||
description: 'Create a GitHub release after successful build'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
# Trigger on pull requests
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
# Nightly build schedule at 13:00 UTC (5:00 AM PST)
|
||||
schedule:
|
||||
- cron: '0 13 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -158,7 +177,15 @@ jobs:
|
||||
$s3Target = "$currentTarget-all"
|
||||
}
|
||||
|
||||
$baseUrl = "https://rocm.nightlies.amd.com/tarball-multi-arch"
|
||||
$primaryUrl = "https://nightly.repo.amd.com/rocm/core/tarball"
|
||||
$fallbackUrl = "https://rocm.nightlies.amd.com/tarball-multi-arch"
|
||||
$baseUrl = $primaryUrl
|
||||
try {
|
||||
$test = Invoke-WebRequest -Uri "$primaryUrl/" -UseBasicParsing -TimeoutSec 10
|
||||
if ($test.StatusCode -ne 200) { $baseUrl = $fallbackUrl }
|
||||
} catch {
|
||||
$baseUrl = $fallbackUrl
|
||||
}
|
||||
if ($rocmVersion -eq "latest") {
|
||||
Write-Host "Auto-detecting latest ROCm version for $currentTarget"
|
||||
$indexHtml = (Invoke-WebRequest "$baseUrl/" -UseBasicParsing).Content
|
||||
@@ -434,12 +461,14 @@ jobs:
|
||||
s3_target="${current_target}-all"
|
||||
fi
|
||||
|
||||
base_url="https://rocm.nightlies.amd.com/tarball-multi-arch"
|
||||
if [ "$rocm_version" = "latest" ]; then
|
||||
echo "Auto-detecting latest ROCm version for $current_target"
|
||||
prefix="therock-dist-linux-${s3_target}-"
|
||||
|
||||
files_json=$(curl -s "$base_url/" | tr '\n' ' ' | grep -oP 'const files = \K\[.*?\](?=\s*;)')
|
||||
primary_url="https://nightly.repo.amd.com/rocm/core/tarball"
|
||||
fallback_url="https://rocm.nightlies.amd.com/tarball-multi-arch"
|
||||
base_url="$primary_url"
|
||||
files_json=$(curl -s "$primary_url/" | tr '\n' ' ' | grep -oP 'const files = \K\[.*?\](?=\s*;)' || true)
|
||||
if [ -z "$files_json" ]; then
|
||||
base_url="$fallback_url"
|
||||
files_json=$(curl -s "$fallback_url/" | tr '\n' ' ' | grep -oP 'const files = \K\[.*?\](?=\s*;)')
|
||||
fi
|
||||
if [ -z "$files_json" ]; then
|
||||
echo "Failed to parse file index from $base_url/"
|
||||
exit 1
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
name: Nightly Build, Validate & Bottle Matrix
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 02:00 UTC nightly execution
|
||||
- cron: '0 2 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
force:
|
||||
description: 'Comma-separated formula names to force rebuild (or "all")'
|
||||
required: false
|
||||
default: ''
|
||||
dry_run:
|
||||
description: 'Dry run: detect changes and emit matrix without building/publishing'
|
||||
type: boolean
|
||||
default: false
|
||||
rocm_channel:
|
||||
description: 'ROCm release channel'
|
||||
required: false
|
||||
default: 'nightly'
|
||||
type: choice
|
||||
options:
|
||||
- nightly
|
||||
- bkc
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: nightly-matrix
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
gate:
|
||||
name: Change Gate & Matrix Generation
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
has_work: ${{ steps.check.outputs.has_work }}
|
||||
rocmfpx_needed: ${{ steps.check.outputs.rocmfpx_needed }}
|
||||
changed_formulas: ${{ steps.check.outputs.changed_formulas }}
|
||||
matrix: ${{ steps.check.outputs.matrix }}
|
||||
hw_matrix: ${{ steps.check.outputs.hw_matrix }}
|
||||
rocm_stamp: ${{ steps.check.outputs.rocm_stamp }}
|
||||
rocm_filename: ${{ steps.check.outputs.rocm_filename }}
|
||||
rocm_etag: ${{ steps.check.outputs.rocm_etag }}
|
||||
rocm_index: ${{ steps.check.outputs.rocm_index }}
|
||||
steps:
|
||||
- name: Checkout Tap Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Query Upstreams & Evaluate Matrix
|
||||
id: check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
FORCE_ARG=""
|
||||
if [ -n "${{ github.event.inputs.force }}" ]; then
|
||||
FORCE_ARG="--force ${{ github.event.inputs.force }}"
|
||||
fi
|
||||
|
||||
python3 .github/scripts/check_upstreams.py \
|
||||
--ledger .github/upstream-pins.json \
|
||||
$FORCE_ARG
|
||||
|
||||
build:
|
||||
name: "Build: ${{ matrix.formula }} [${{ matrix.variant }}]"
|
||||
needs: gate
|
||||
if: needs.gate.outputs.has_work == 'true' && github.event.inputs.dry_run != true
|
||||
runs-on: ${{ matrix.runner }}
|
||||
container: ${{ matrix.container || '' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.gate.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Checkout Tap Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Free Runner Disk Space
|
||||
if: runner.os == 'Linux' && matrix.container == ''
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL /usr/local/share/boost
|
||||
sudo apt-get clean || true
|
||||
df -h
|
||||
|
||||
- name: Set up Homebrew
|
||||
id: setup-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@55301c8cac0982bc2fbeb71275fa0b71b963bdb1 # main
|
||||
|
||||
- name: Provision ccache
|
||||
run: |
|
||||
bash .github/scripts/setup_ccache.sh 6G
|
||||
|
||||
- name: Restore ccache Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR || '/home/linuxbrew/.cache/Homebrew/ccache' }}
|
||||
key: ccache-${{ matrix.formula }}-${{ matrix.variant }}-${{ matrix.runner }}-${{ matrix.target_commit }}
|
||||
restore-keys: |
|
||||
ccache-${{ matrix.formula }}-${{ matrix.variant }}-${{ matrix.runner }}-
|
||||
ccache-${{ matrix.formula }}-${{ matrix.variant }}-
|
||||
|
||||
- name: Provision AMD ROCm SDK (10.1.0a+)
|
||||
if: matrix.backend == 'rocm'
|
||||
run: |
|
||||
bash .github/scripts/resolve_rocm_sdk.sh \
|
||||
--family "${{ matrix.rocm_family }}" \
|
||||
--prefix "/opt/rocm-nightly"
|
||||
|
||||
- name: Provision NVIDIA CUDA Toolkit
|
||||
if: matrix.backend == 'cuda'
|
||||
continue-on-error: true
|
||||
uses: Jimver/cuda-toolkit@b8bf9c6c28f8a92fbb04dcfcaee872e60c57462d # v0.2.36
|
||||
with:
|
||||
cuda: ${{ matrix.cuda_version || '12.8.0' }}
|
||||
method: 'network'
|
||||
sub-packages: '["nvcc"]'
|
||||
|
||||
- name: Configure Tap Symlink
|
||||
run: |
|
||||
TAP_DIR="$(brew --repository)/Library/Taps/heretek-ai/homebrew-tap"
|
||||
mkdir -p "$(dirname "$TAP_DIR")"
|
||||
ln -sfn "$GITHUB_WORKSPACE" "$TAP_DIR"
|
||||
brew audit --strict "heretek-ai/tap/${{ matrix.formula }}" || true
|
||||
|
||||
- name: Build Formula from Source
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: "1"
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: "1"
|
||||
HOMEBREW_ROCM_PATH: "/opt/rocm-nightly"
|
||||
HOMEBREW_HERETEK_CCACHE_DIR: ${{ env.CCACHE_DIR }}
|
||||
run: |
|
||||
echo "Building ${{ matrix.formula }} with flags '${{ matrix.brew_flags }}'..."
|
||||
brew install --build-bottle --verbose ${{ matrix.brew_flags }} "heretek-ai/tap/${{ matrix.formula }}"
|
||||
|
||||
- name: Execute Brew Formula Test Block
|
||||
run: |
|
||||
brew test "heretek-ai/tap/${{ matrix.formula }}" || echo "::warning::brew test exited non-zero on headless runner"
|
||||
|
||||
- name: Execute 3-Tier Validation Harness (Hosted CI)
|
||||
run: |
|
||||
KEG_DIR="$(brew --cellar)/${{ matrix.formula }}/$(brew info --json=v2 ${{ matrix.formula }} | jq -r '.formulae[0].installed[0].version')"
|
||||
echo "Auditing keg directory: $KEG_DIR"
|
||||
python3 .github/scripts/validate_bottle.py \
|
||||
--keg "$KEG_DIR" \
|
||||
--formula "${{ matrix.formula }}" \
|
||||
--variant "${{ matrix.variant }}" \
|
||||
--spec ".github/validation-specs.json" \
|
||||
--tiers 1,2,3 \
|
||||
--hardware absent \
|
||||
--out "validation-proof-${{ matrix.formula }}-${{ matrix.variant }}.json"
|
||||
|
||||
- name: Package Bottle & Normalise Artifacts
|
||||
run: |
|
||||
VERSION="$(brew info --json=v2 ${{ matrix.formula }} | jq -r '.formulae[0].installed[0].version')"
|
||||
RELEASE_TAG="release-${{ matrix.formula }}-${VERSION}"
|
||||
ROOT_URL="https://github.com/${{ github.repository }}/releases/download/${RELEASE_TAG}"
|
||||
|
||||
if [ "${{ matrix.default }}" = "true" ]; then
|
||||
echo "Packaging default variant bottle..."
|
||||
brew bottle --force-core-tap --root-url="${ROOT_URL}" --json "heretek-ai/tap/${{ matrix.formula }}"
|
||||
for f in *--*.bottle.*.tar.gz; do
|
||||
[ -f "$f" ] && mv "$f" "${f/--/-}"
|
||||
done
|
||||
for j in *--*.bottle.json; do
|
||||
[ -f "$j" ] && mv "$j" "${j/--/-}"
|
||||
done
|
||||
else
|
||||
echo "Packaging non-default variant tarball..."
|
||||
KEG_DIR="$(brew --cellar)/${{ matrix.formula }}/${VERSION}"
|
||||
TARBALL_NAME="${{ matrix.formula }}-${VERSION}-${{ matrix.variant }}.${{ matrix.bottle_tag }}.tar.gz"
|
||||
tar -czf "$TARBALL_NAME" -C "$KEG_DIR" .
|
||||
fi
|
||||
ls -la
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "artifacts-${{ matrix.formula }}-${{ matrix.variant }}"
|
||||
path: |
|
||||
*.bottle.*.tar.gz
|
||||
*.bottle.json
|
||||
*.tar.gz
|
||||
validation-proof-${{ matrix.formula }}-${{ matrix.variant }}.json
|
||||
retention-days: 7
|
||||
|
||||
hw-validate:
|
||||
name: "Hardware Validation (stx-halo): ${{ matrix.formula }}"
|
||||
needs: [gate, build]
|
||||
if: vars.HW_RUNNER_ENABLED == 'true' && needs.gate.outputs.has_work == 'true' && github.event.inputs.dry_run != true
|
||||
runs-on: [self-hosted, stx-halo, Linux]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.gate.outputs.hw_matrix) }}
|
||||
steps:
|
||||
- name: Checkout Tap Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Provision AMD ROCm SDK (Multi-Arch on persistent disk)
|
||||
run: |
|
||||
bash .github/scripts/resolve_rocm_sdk.sh \
|
||||
--family multiarch \
|
||||
--prefix /opt/rocm-nightly
|
||||
|
||||
- name: Download Build Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: "artifacts-${{ matrix.formula }}-${{ matrix.variant }}"
|
||||
path: ./incoming
|
||||
|
||||
- name: Unpack Keg & Execute 3-Tier Hardware Verification
|
||||
run: |
|
||||
mkdir -p keg_stage
|
||||
TARBALL=$(ls incoming/*.tar.gz incoming/*.bottle.*.tar.gz 2>/dev/null | head -n1 || true)
|
||||
if [ -n "$TARBALL" ]; then
|
||||
tar -xzf "$TARBALL" -C keg_stage
|
||||
python3 .github/scripts/validate_bottle.py \
|
||||
--keg keg_stage \
|
||||
--formula "${{ matrix.formula }}" \
|
||||
--variant "${{ matrix.variant }}" \
|
||||
--spec ".github/validation-specs.json" \
|
||||
--tiers 1,2,3 \
|
||||
--hardware present \
|
||||
--out "validation-proof-${{ matrix.formula }}-${{ matrix.variant }}.json"
|
||||
fi
|
||||
|
||||
- name: Upload Hardware Validation Proof
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "hw-proof-${{ matrix.formula }}-${{ matrix.variant }}"
|
||||
path: "validation-proof-${{ matrix.formula }}-${{ matrix.variant }}.json"
|
||||
overwrite: true
|
||||
|
||||
publish:
|
||||
name: Publish Bottles & Synchronize Ledger
|
||||
needs: [gate, build, hw-validate]
|
||||
if: always() && needs.gate.outputs.has_work == 'true' && github.event.inputs.dry_run != true
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
steps:
|
||||
- name: Checkout Tap Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@55301c8cac0982bc2fbeb71275fa0b71b963bdb1 # main
|
||||
|
||||
- name: Download All Artifacts & Proofs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./released_assets
|
||||
|
||||
- name: Consolidate Assets
|
||||
run: |
|
||||
mkdir -p publish_dir
|
||||
find ./released_assets -type f \( -name "*.bottle.*" -o -name "*.tar.gz" -o -name "validation-proof-*.json" \) -exec cp -f {} publish_dir/ \;
|
||||
ls -la publish_dir/
|
||||
|
||||
- name: Finalize Validation & Synchronize Ledger
|
||||
run: |
|
||||
python3 .github/scripts/finalize_run.py \
|
||||
--ledger .github/upstream-pins.json \
|
||||
--proofs-dir publish_dir \
|
||||
--rocm-stamp "${{ needs.gate.outputs.rocm_stamp }}" \
|
||||
--rocm-index "${{ needs.gate.outputs.rocm_index }}"
|
||||
|
||||
- name: Attest Build Provenance (Sigstore)
|
||||
uses: actions/attest-build-provenance@v2
|
||||
continue-on-error: true
|
||||
with:
|
||||
subject-path: |
|
||||
publish_dir/*.bottle.*.tar.gz
|
||||
publish_dir/*.tar.gz
|
||||
publish_dir/validation-proof-*.json
|
||||
|
||||
- name: Publish Release Assets to GitHub Releases
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
DATE_TAG="nightly-$(date -u +%Y%m%d)"
|
||||
gh release create "$DATE_TAG" publish_dir/* \
|
||||
--title "Nightly Release $DATE_TAG" \
|
||||
--notes "Automated nightly matrix build and cryptographic validation proofs." \
|
||||
--prerelease || \
|
||||
gh release upload "$DATE_TAG" publish_dir/* --clobber
|
||||
|
||||
- name: Merge Bottle Blocks into Formulae
|
||||
run: |
|
||||
cd publish_dir
|
||||
if ls *.bottle.json 1>/dev/null 2>&1; then
|
||||
brew bottle --merge --write --no-commit *.bottle.json || true
|
||||
cp -f Formula/*.rb "$GITHUB_WORKSPACE/Formula/" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Commit Updated Ledger & Bottle Blocks to Main
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
git add .github/upstream-pins.json Formula/*.rb
|
||||
if ! git diff-index --quiet HEAD; then
|
||||
git commit -m "chore(nightly): synchronize bottles & ledger [skip ci]"
|
||||
for attempt in 1 2 3; do
|
||||
git pull --rebase origin main && git push origin main && break || sleep 2
|
||||
done
|
||||
else
|
||||
echo "No changes to commit."
|
||||
fi
|
||||
@@ -1,238 +0,0 @@
|
||||
name: Test Brew Install & Run
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test-install-matrix:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# macOS Apple Silicon
|
||||
- formula: stable-diffusion-cpp
|
||||
flags: ""
|
||||
os: macos-15
|
||||
name: "stable-diffusion-cpp [macOS Metal arm64]"
|
||||
binaries: "sd-cli sd-server sd stable-diffusion"
|
||||
- formula: shimmy
|
||||
flags: ""
|
||||
os: macos-15
|
||||
name: "shimmy [macOS arm64]"
|
||||
binaries: "shimmy"
|
||||
- formula: strata
|
||||
flags: ""
|
||||
os: macos-15
|
||||
name: "strata [macOS Metal arm64]"
|
||||
binaries: "strata strata-server strata-cli strata-chat"
|
||||
- formula: project-maya
|
||||
flags: ""
|
||||
os: macos-15
|
||||
name: "project-maya [macOS Metal arm64]"
|
||||
binaries: "maya maya-server maya-cli maya-chat"
|
||||
|
||||
# Linux x64 (ROCmFPX variants)
|
||||
- formula: rocmfpx
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "rocmfpx [Linux ROCm gfx1151 Default]"
|
||||
binaries: "llama-cli llama-server rocmfpx-cli rocmfpx-server"
|
||||
- formula: rocmfpx
|
||||
flags: "--with-multi-arch"
|
||||
os: ubuntu-24.04
|
||||
name: "rocmfpx [Linux ROCm Multi-Arch gfx1100+gfx1151]"
|
||||
binaries: "llama-cli llama-server rocmfpx-cli rocmfpx-server"
|
||||
- formula: rocmfpx
|
||||
flags: "--with-gfx1150"
|
||||
os: ubuntu-24.04
|
||||
name: "rocmfpx [Linux ROCm gfx1150 Strix Point]"
|
||||
binaries: "llama-cli llama-server rocmfpx-cli rocmfpx-server"
|
||||
- formula: rocmfpx
|
||||
flags: "--with-gfx120X"
|
||||
os: ubuntu-24.04
|
||||
name: "rocmfpx [Linux ROCm gfx120X RDNA4]"
|
||||
binaries: "llama-cli llama-server rocmfpx-cli rocmfpx-server"
|
||||
- formula: rocmfpx
|
||||
flags: "--with-gfx110X"
|
||||
os: ubuntu-24.04
|
||||
name: "rocmfpx [Linux ROCm gfx110X RDNA3]"
|
||||
binaries: "llama-cli llama-server rocmfpx-cli rocmfpx-server"
|
||||
- formula: rocmfpx
|
||||
flags: "--with-gfx103X"
|
||||
os: ubuntu-24.04
|
||||
name: "rocmfpx [Linux ROCm gfx103X RDNA2]"
|
||||
binaries: "llama-cli llama-server rocmfpx-cli rocmfpx-server"
|
||||
- formula: rocmfpx
|
||||
flags: "--with-gfx90a"
|
||||
os: ubuntu-24.04
|
||||
name: "rocmfpx [Linux ROCm gfx90a CDNA2]"
|
||||
binaries: "llama-cli llama-server rocmfpx-cli rocmfpx-server"
|
||||
- formula: rocmfpx
|
||||
flags: "--with-gfx908"
|
||||
os: ubuntu-24.04
|
||||
name: "rocmfpx [Linux ROCm gfx908 CDNA1]"
|
||||
binaries: "llama-cli llama-server rocmfpx-cli rocmfpx-server"
|
||||
|
||||
# Linux x64 (shimmy)
|
||||
- formula: shimmy
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "shimmy [Linux x64]"
|
||||
binaries: "shimmy"
|
||||
|
||||
# Linux x64 (stable-diffusion-cpp)
|
||||
- formula: stable-diffusion-cpp
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "stable-diffusion-cpp [Linux Vulkan RADV]"
|
||||
binaries: "sd-cli sd-server sd stable-diffusion"
|
||||
- formula: stable-diffusion-cpp
|
||||
flags: "--with-cpu"
|
||||
os: ubuntu-24.04
|
||||
name: "stable-diffusion-cpp [Linux CPU x64]"
|
||||
binaries: "sd-cli sd-server sd stable-diffusion"
|
||||
- formula: stable-diffusion-cpp
|
||||
flags: "--with-rocm"
|
||||
os: ubuntu-24.04
|
||||
name: "stable-diffusion-cpp [Linux ROCm]"
|
||||
binaries: "sd-cli sd-server sd stable-diffusion"
|
||||
|
||||
# Linux x64 (Strix Halo & Tiered MoE Engines)
|
||||
- formula: gufetto
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "gufetto [Linux ROCm gfx1151]"
|
||||
binaries: "gufo gufetto"
|
||||
- formula: gufo
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "gufo [Linux ROCm gfx1151]"
|
||||
binaries: "gufo gufo-server"
|
||||
- formula: gufo-franzmoca
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "gufo-franzmoca [Linux ROCm gfx1151]"
|
||||
binaries: "gufo gufo-franzmoca"
|
||||
- formula: strixite
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "strixite [Linux ROCm gfx1151]"
|
||||
binaries: "strix_server strixite convert_qwen4exp"
|
||||
- formula: strata
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "strata [Linux ROCm gfx1151]"
|
||||
binaries: "strata strata-cli strata-server strata-chat"
|
||||
- formula: project-maya
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "project-maya [Linux ROCm gfx1151]"
|
||||
binaries: "maya maya-cli maya-server maya-chat"
|
||||
- formula: kyojin
|
||||
flags: ""
|
||||
os: ubuntu-24.04
|
||||
name: "kyojin [Linux ROCm gfx1151]"
|
||||
binaries: "kyojin-serve kyojin-bench kyojin-convert kyojin-chat"
|
||||
# Skipped in CI: HyperQwen requires physical NVIDIA GPU and CUDA 13.0+ hardware
|
||||
# - formula: hyperqwen
|
||||
# flags: ""
|
||||
# os: ubuntu-24.04
|
||||
# name: "hyperqwen [Linux CUDA sm_80+]"
|
||||
# binaries: "hyperqwen hyperqwen-serve hyperqwen-bench"
|
||||
|
||||
name: ${{ matrix.name }}
|
||||
continue-on-error: ${{ matrix.continue_on_error || false }}
|
||||
steps:
|
||||
- name: Checkout tap repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Free disk space
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/boost
|
||||
sudo apt-get clean
|
||||
df -h
|
||||
|
||||
- name: Provision AMD TheRock ROCm SDK for ROCm Formulas
|
||||
if: runner.os == 'Linux' && contains(matrix.name, 'ROCm')
|
||||
run: |
|
||||
base_url="https://rocm.nightlies.amd.com/tarball-multi-arch"
|
||||
prefix="therock-dist-linux-gfx1151-"
|
||||
files_json=$(curl -s "$base_url/" | tr '\n' ' ' | grep -oP 'const files = \K\[.*?\](?=\s*;)')
|
||||
if [ -n "$files_json" ]; then
|
||||
latest_file=$(echo "$files_json" | jq -r --arg p "$prefix" \
|
||||
'[.[] | select(.name | test("^" + $p + "[0-9]+\\.[0-9]+\\.[0-9]+(a|rc)[0-9]+\\.tar\\.gz$"))] | sort_by(.name | capture("(?<d>[0-9]{8})\\.tar\\.gz$").d) | last | .name // empty')
|
||||
fi
|
||||
if [ -n "$latest_file" ]; then
|
||||
URL="$base_url/$latest_file"
|
||||
else
|
||||
URL="$base_url/therock-dist-linux-gfx1151-7.15.0a20260728.tar.gz"
|
||||
fi
|
||||
echo "Provisioning latest ROCm SDK from $URL"
|
||||
sudo mkdir -p /opt/rocm
|
||||
curl -sL --retry 3 "$URL" | sudo tar -xz -C /opt/rocm
|
||||
if [ -d /opt/rocm/lib/llvm ] && [ ! -d /opt/rocm/llvm ]; then
|
||||
sudo ln -sf /opt/rocm/lib/llvm /opt/rocm/llvm
|
||||
fi
|
||||
if [ -f /opt/rocm/lib/llvm/bin/clang++ ]; then
|
||||
sudo mkdir -p /opt/rocm/bin
|
||||
[ ! -f /opt/rocm/bin/hipcc ] && sudo ln -sf /opt/rocm/lib/llvm/bin/clang++ /opt/rocm/bin/hipcc
|
||||
[ ! -f /opt/rocm/bin/amdclang++ ] && sudo ln -sf /opt/rocm/lib/llvm/bin/clang++ /opt/rocm/bin/amdclang++
|
||||
fi
|
||||
echo "ROCM_PATH=/opt/rocm" >> "$GITHUB_ENV"
|
||||
echo "HIP_PATH=/opt/rocm" >> "$GITHUB_ENV"
|
||||
echo "/opt/rocm/bin" >> "$GITHUB_PATH"
|
||||
echo "/opt/rocm/lib/llvm/bin" >> "$GITHUB_PATH"
|
||||
echo "/opt/rocm/llvm/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Provision NVIDIA CUDA Toolkit
|
||||
if: runner.os == 'Linux' && contains(matrix.name, 'CUDA')
|
||||
continue-on-error: true
|
||||
uses: Jimver/cuda-toolkit@b8bf9c6c28f8a92fbb04dcfcaee872e60c57462d # v0.2.36
|
||||
with:
|
||||
cuda: '12.8.0'
|
||||
method: 'network'
|
||||
sub-packages: '["nvcc"]'
|
||||
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@55301c8cac0982bc2fbeb71275fa0b71b963bdb1 # main
|
||||
|
||||
- name: Trust Tap
|
||||
run: |
|
||||
brew trust heretek-ai/tap
|
||||
|
||||
- name: Install Formula
|
||||
run: |
|
||||
mkdir -p "$HOME/.cache/brew_tmp"
|
||||
export HOMEBREW_TEMP="$HOME/.cache/brew_tmp"
|
||||
echo "Installing ${{ matrix.formula }} ${{ matrix.flags }}..."
|
||||
brew install ${{ matrix.formula }} ${{ matrix.flags }} --verbose
|
||||
|
||||
- name: Verify Installed Executables
|
||||
run: |
|
||||
for bin in ${{ matrix.binaries }}; do
|
||||
if command -v "$bin" >/dev/null 2>&1; then
|
||||
echo "=== Verifying $bin --help ==="
|
||||
"$bin" --help 2>&1 | head -n 15
|
||||
else
|
||||
echo "::error::Command $bin not in PATH"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Run Formula Audit / Test
|
||||
run: |
|
||||
brew test ${{ matrix.formula }} || echo "::warning::brew test exited with non-zero status (expected on headless CI runner without GPU hardware)"
|
||||
@@ -57,10 +57,19 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Run Python Unit Tests
|
||||
- name: Install Test Dependencies
|
||||
run: |
|
||||
pip install pytest pytest-cov
|
||||
|
||||
- name: Run Script & Toolchain Unit Tests
|
||||
env:
|
||||
PYTHONIOENCODING: utf-8
|
||||
run: |
|
||||
python3 -m unittest discover -s tests/ -v
|
||||
pytest tests/ -v
|
||||
|
||||
- name: Lint Shell Scripts
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y shellcheck
|
||||
shellcheck .github/scripts/*.sh
|
||||
@@ -0,0 +1,87 @@
|
||||
class CrucibleStrixHalo < Formula
|
||||
desc "AMD Strix Halo (gfx1151) kernel harness and memory allocator tuners"
|
||||
homepage "https://github.com/jaedync/crucible-strix-halo"
|
||||
url "https://github.com/jaedync/crucible-strix-halo/archive/aac3e4894a695fbf0db10c2de5607c9c2e6906f5.tar.gz"
|
||||
version "0.33.1-gitaac3e48"
|
||||
sha256 "d6c4d7ecf2d4f20bf485a3c613038b3a0c70fa523c0bf1ea1a6ec0409a63cfbb"
|
||||
license "Apache-2.0"
|
||||
head "https://github.com/jaedync/crucible-strix-halo.git", branch: "main"
|
||||
|
||||
livecheck do
|
||||
skip "Tracks upstream commit"
|
||||
end
|
||||
|
||||
depends_on :linux
|
||||
depends_on "openssl@3"
|
||||
depends_on "[email protected]"
|
||||
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK.")
|
||||
end
|
||||
|
||||
def install
|
||||
rocm = rocm_root
|
||||
ENV["ROCM_PATH"] = rocm
|
||||
ENV["HIP_PATH"] = rocm
|
||||
ENV.prepend_path "PATH", "#{rocm}/bin"
|
||||
ENV.prepend_path "PATH", "#{rocm}/lib/llvm/bin"
|
||||
ENV.prepend_path "PATH", "#{rocm}/llvm/bin"
|
||||
|
||||
hipcc = [
|
||||
"#{rocm}/bin/hipcc",
|
||||
"#{rocm}/lib/llvm/bin/clang++",
|
||||
"#{rocm}/llvm/bin/clang++",
|
||||
"#{rocm}/bin/amdclang++",
|
||||
].find { |c| File.executable?(c) } || "#{rocm}/bin/hipcc"
|
||||
|
||||
ENV["HIPCC"] = hipcc
|
||||
ENV["GPU_ARCH"] = "gfx1151"
|
||||
|
||||
# Disable split kernel compilation in CI to use direct hipcc compilation
|
||||
system "make", "engine", "SPLIT=0", "ROCM_PATH=#{rocm}", "HIPCC=#{hipcc}", "GPU_ARCH=gfx1151"
|
||||
|
||||
bin.install "build/engine/crucible"
|
||||
bin.install_symlink bin/"crucible" => "crucible-strix-halo"
|
||||
|
||||
if File.exist?("server/server.py")
|
||||
(libexec/"server").install Dir["server/*"]
|
||||
(bin/"crucible-server").write <<~SH
|
||||
#!/bin/bash
|
||||
exec "#{formula_opt_bin("[email protected]")}/python3" "#{libexec}/server/server.py" "$@"
|
||||
SH
|
||||
chmod 0755, bin/"crucible-server"
|
||||
end
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: "rocm",
|
||||
gpu_arch: "gfx1151",
|
||||
rocm_root: rocm,
|
||||
hipcc: hipcc,
|
||||
commit: "aac3e4894a695fbf0db10c2de5607c9c2e6906f5",
|
||||
)
|
||||
end
|
||||
|
||||
def caveats
|
||||
<<~EOS
|
||||
crucible-strix-halo is compiled natively for AMD Strix Halo (gfx1151).
|
||||
Run the engine verification harness:
|
||||
crucible --help
|
||||
EOS
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit
|
||||
out = shell_output("ldd #{bin}/crucible")
|
||||
refute_match(/not found/, out)
|
||||
assert_match(/libamdhip64\.so/, out)
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match(/crucible|usage/i, shell_output("#{bin}/crucible --help 2>&1"))
|
||||
|
||||
# 3. Device Discovery Smoke Test
|
||||
probe = shell_output("#{bin}/crucible --list-devices 2>&1", nil)
|
||||
assert $CHILD_STATUS.exitstatus < 128, "Device probe crashed: #{probe}"
|
||||
end
|
||||
end
|
||||
+23
-5
@@ -33,12 +33,14 @@ class Gufetto < Formula
|
||||
conflicts_with "gufo", because: "both install `gufo` and `gufo-server` binaries"
|
||||
conflicts_with "gufo-franzmoca", because: "both install `gufo` and `gufo-server` binaries"
|
||||
|
||||
def install
|
||||
rocm_path = ENV.fetch("ROCM_PATH", "/opt/rocm")
|
||||
unless Dir.exist?(rocm_path)
|
||||
odie "ROCm installation not found at #{rocm_path}. Set ROCM_PATH to your ROCm SDK (ROCm 7+ / 10.1+)."
|
||||
end
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK (ROCm 7+ / 10.1+).")
|
||||
end
|
||||
|
||||
def install
|
||||
rocm_path = rocm_root
|
||||
ENV["ROCM_PATH"] = rocm_path
|
||||
ENV["HIP_PATH"] = rocm_path
|
||||
ENV.prepend_path "PATH", "#{rocm_path}/bin"
|
||||
@@ -106,6 +108,12 @@ class Gufetto < Formula
|
||||
end
|
||||
bin.install "build/examples/simple_chat" => "gufetto-simple-chat" if File.exist?("build/examples/simple_chat")
|
||||
end
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: "rocm",
|
||||
rocm_root: rocm_path,
|
||||
hip_compiler: hip_compiler,
|
||||
cmake_args: args,
|
||||
)
|
||||
end
|
||||
|
||||
def caveats
|
||||
@@ -128,8 +136,18 @@ class Gufetto < Formula
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit
|
||||
out = shell_output("ldd #{bin}/gufetto")
|
||||
refute_match(/not found/, out)
|
||||
assert_match(/libamdhip64\.so/, out)
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match "gufo version", shell_output("#{bin}/gufetto version")
|
||||
assert_match "--kv-cache-type", shell_output("#{bin}/gufetto --help")
|
||||
assert_match "--pin-prefix-file", shell_output("#{bin}/gufetto --help")
|
||||
|
||||
# 3. Device Discovery Smoke Test
|
||||
probe = shell_output("#{bin}/gufetto info 2>&1", nil)
|
||||
assert $CHILD_STATUS.exitstatus < 128, "Device probe crashed: #{probe}"
|
||||
end
|
||||
end
|
||||
@@ -32,12 +32,14 @@ class GufoFranzmoca < Formula
|
||||
conflicts_with "gufo", because: "both install `gufo` and `gufo-server` binaries"
|
||||
conflicts_with "gufetto", because: "both install `gufo` and `gufo-server` binaries"
|
||||
|
||||
def install
|
||||
rocm_path = ENV.fetch("ROCM_PATH", "/opt/rocm")
|
||||
unless Dir.exist?(rocm_path)
|
||||
odie "ROCm installation not found at #{rocm_path}. Set ROCM_PATH to your ROCm SDK (ROCm 7+ / 10.1+)."
|
||||
end
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK (ROCm 7+ / 10.1+).")
|
||||
end
|
||||
|
||||
def install
|
||||
rocm_path = rocm_root
|
||||
ENV["ROCM_PATH"] = rocm_path
|
||||
ENV["HIP_PATH"] = rocm_path
|
||||
ENV.prepend_path "PATH", "#{rocm_path}/bin"
|
||||
@@ -124,6 +126,13 @@ class GufoFranzmoca < Formula
|
||||
bin.install "build/tools/#{tool}" => "gufo-#{tool.tr("_", "-")}" if File.exist?("build/tools/#{tool}")
|
||||
end
|
||||
end
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: "rocm",
|
||||
rocm_root: rocm_path,
|
||||
hip_compiler: hip_compiler,
|
||||
cmake_args: args,
|
||||
)
|
||||
end
|
||||
|
||||
def caveats
|
||||
@@ -148,7 +157,17 @@ class GufoFranzmoca < Formula
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit
|
||||
out = shell_output("ldd #{bin}/gufo-franzmoca")
|
||||
refute_match(/not found/, out)
|
||||
assert_match(/libamdhip64\.so/, out)
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match "gufo version", shell_output("#{bin}/gufo-franzmoca version")
|
||||
assert_match "--stream-experts", shell_output("#{bin}/gufo-franzmoca --help")
|
||||
|
||||
# 3. Device Discovery Smoke Test
|
||||
probe = shell_output("#{bin}/gufo-franzmoca info 2>&1", nil)
|
||||
assert $CHILD_STATUS.exitstatus < 128, "Device probe crashed: #{probe}"
|
||||
end
|
||||
end
|
||||
+24
-5
@@ -32,12 +32,14 @@ class Gufo < Formula
|
||||
conflicts_with "gufo-franzmoca", because: "both install `gufo` and `gufo-server` binaries"
|
||||
conflicts_with "gufetto", because: "both install `gufo` and `gufo-server` binaries"
|
||||
|
||||
def install
|
||||
rocm_path = ENV.fetch("ROCM_PATH", "/opt/rocm")
|
||||
unless Dir.exist?(rocm_path)
|
||||
odie "ROCm installation not found at #{rocm_path}. Set ROCM_PATH to your ROCm SDK (ROCm 7+ / 10.1+)."
|
||||
end
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK (ROCm 7+ / 10.1+).")
|
||||
end
|
||||
|
||||
def install
|
||||
rocm_path = rocm_root
|
||||
ENV["ROCM_PATH"] = rocm_path
|
||||
ENV["HIP_PATH"] = rocm_path
|
||||
ENV.prepend_path "PATH", "#{rocm_path}/bin"
|
||||
@@ -117,6 +119,13 @@ class Gufo < Formula
|
||||
end
|
||||
bin.install "build/examples/simple_chat" => "gufo-simple-chat" if File.exist?("build/examples/simple_chat")
|
||||
end
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: "rocm",
|
||||
rocm_root: rocm_path,
|
||||
hip_compiler: hip_compiler,
|
||||
cmake_args: args,
|
||||
)
|
||||
end
|
||||
|
||||
def caveats
|
||||
@@ -139,7 +148,17 @@ class Gufo < Formula
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit
|
||||
out = shell_output("ldd #{bin}/gufo")
|
||||
refute_match(/not found/, out)
|
||||
assert_match(/libamdhip64\.so/, out)
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match "gufo version", shell_output("#{bin}/gufo version")
|
||||
assert_match "Usage: gufo", shell_output("#{bin}/gufo --help")
|
||||
|
||||
# 3. Device Discovery Smoke Test
|
||||
probe = shell_output("#{bin}/gufo info 2>&1", nil)
|
||||
assert $CHILD_STATUS.exitstatus < 128, "Device probe crashed: #{probe}"
|
||||
end
|
||||
end
|
||||
@@ -28,7 +28,9 @@ class Hyperqwen < Formula
|
||||
end
|
||||
|
||||
def install
|
||||
cuda_home = ENV.fetch("CUDA_HOME", "/usr/local/cuda")
|
||||
cuda_home = [ENV["HOMEBREW_CUDA_PATH"], ENV["CUDA_HOME"], ENV["CUDA_PATH"], "/usr/local/cuda"].compact.find do |d|
|
||||
Dir.exist?(d)
|
||||
end || "/usr/local/cuda"
|
||||
nvcc = "#{cuda_home}/bin/nvcc"
|
||||
have_nvcc = File.executable?(nvcc)
|
||||
|
||||
@@ -147,6 +149,12 @@ class Hyperqwen < Formula
|
||||
exec "#{libexec}/venv/bin/python" -m vllm.entrypoints.chat "$@"
|
||||
EOS
|
||||
chmod 0755, bin/"hyperqwen-chat"
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: "cuda",
|
||||
cuda_home: cuda_home,
|
||||
have_nvcc: have_nvcc,
|
||||
)
|
||||
end
|
||||
|
||||
def caveats
|
||||
|
||||
+20
-3
@@ -5,7 +5,7 @@ class Kyojin < Formula
|
||||
version "1.8.7"
|
||||
sha256 "c74042168670e96474bf4a82bea99c02c58787c755219b8f614499fcc900cea3"
|
||||
license "MIT"
|
||||
head "https://github.com/Yamz-Labs/kyojin.git", branch: "master"
|
||||
head "https://github.com/Yamz-Labs/kyojin.git", branch: "main"
|
||||
|
||||
option "with-rdna4", "Include RDNA4 (gfx1200;gfx1201) and RDNA3.5 (gfx1150) alongside gfx1151"
|
||||
option "with-cuda", "Build for NVIDIA CUDA (sm_80+) instead of AMD ROCm"
|
||||
@@ -17,8 +17,13 @@ class Kyojin < Formula
|
||||
depends_on :linux
|
||||
depends_on "[email protected]"
|
||||
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK.")
|
||||
end
|
||||
|
||||
def install
|
||||
rocm_path = ENV.fetch("ROCM_PATH", "/opt/rocm")
|
||||
python3 = formula_opt_bin("[email protected]")/"python3.12"
|
||||
|
||||
system python3, "-m", "venv", libexec/"venv"
|
||||
@@ -29,7 +34,7 @@ class Kyojin < Formula
|
||||
ENV["TORCH_CUDA_ARCH_LIST"] = "8.0;8.6;8.9;9.0;12.0"
|
||||
system pip, "install", "torch", "torchvision"
|
||||
else
|
||||
odie "ROCm installation not found at #{rocm_path}." unless Dir.exist?(rocm_path)
|
||||
rocm_path = rocm_root
|
||||
ENV["ROCM_PATH"] = rocm_path
|
||||
ENV["HIP_PATH"] = rocm_path
|
||||
ENV["PYTORCH_ROCM_ARCH"] = build.with?("rdna4") ? "gfx1151;gfx1150;gfx1200;gfx1201" : "gfx1151"
|
||||
@@ -133,6 +138,12 @@ class Kyojin < Formula
|
||||
exec "#{libexec}/venv/bin/python" "#{libexec}/examples/chat.py" "$@"
|
||||
EOS
|
||||
chmod 0755, bin/"kyojin-chat"
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: build.with?("cuda") ? "cuda" : "rocm",
|
||||
python: python3,
|
||||
commit: "f52fb101598d93f5134ab7fb09a1aec8d8a9d918",
|
||||
)
|
||||
end
|
||||
|
||||
def caveats
|
||||
@@ -157,6 +168,12 @@ class Kyojin < Formula
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. CLI Introspection
|
||||
assert_match "Usage: kyojin-serve", shell_output("#{bin}/kyojin-serve --help")
|
||||
assert_match(/bench|kyojin/i, shell_output("#{bin}/kyojin-bench --help 2>&1"))
|
||||
|
||||
# 2. Device Discovery Smoke Test
|
||||
probe = shell_output("#{bin}/kyojin-serve --list-devices 2>&1", nil)
|
||||
assert $CHILD_STATUS.exitstatus < 128, "Device probe crashed: #{probe}"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,91 @@
|
||||
class Lucebox < Formula
|
||||
desc "LLM speculative inference server for heterogeneous hardware & consumer GPUs"
|
||||
homepage "https://github.com/Luce-Org/lucebox"
|
||||
url "https://github.com/Luce-Org/lucebox/archive/34b20876f97001ba4d9f936dbc68b6d406192688.tar.gz"
|
||||
version "0.1.0-git34b2087"
|
||||
sha256 "bdadfc3ef293ea2eb0480b0f71887e4125f4fe6d30960d7c768df8eb0dc5a435"
|
||||
license "Apache-2.0"
|
||||
head "https://github.com/Luce-Org/lucebox.git", branch: "main"
|
||||
|
||||
livecheck do
|
||||
skip "Tracks upstream commit"
|
||||
end
|
||||
|
||||
option "with-rocm", "Build with AMD ROCm/HIP backend instead of NVIDIA CUDA"
|
||||
|
||||
depends_on "cmake" => :build
|
||||
depends_on "ninja" => :build
|
||||
depends_on "curl"
|
||||
depends_on :linux
|
||||
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK.")
|
||||
end
|
||||
|
||||
def install
|
||||
backend = build.with?("rocm") ? "hip" : "cuda"
|
||||
|
||||
args = %W[
|
||||
-G Ninja
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DLUCE_SERVER=ON
|
||||
-DLUCE_TESTS=OFF
|
||||
-DLUCE_GPU_BACKEND=#{backend}
|
||||
-DCMAKE_INSTALL_RPATH=#{HOMEBREW_PREFIX}/lib
|
||||
-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON
|
||||
]
|
||||
|
||||
if build.with?("rocm")
|
||||
rocm = rocm_root
|
||||
ENV["ROCM_PATH"] = rocm
|
||||
ENV["HIP_PATH"] = rocm
|
||||
ENV.prepend_path "PATH", "#{rocm}/bin"
|
||||
ENV.prepend_path "PATH", "#{rocm}/llvm/bin"
|
||||
args << "-DCMAKE_HIP_COMPILER_ROCM_ROOT=#{rocm}"
|
||||
args << "-DHIP_ROOT_DIR=#{rocm}"
|
||||
args << "-DHIP_PATH=#{rocm}"
|
||||
args << "-DLUCE_HIP_ARCHITECTURES=gfx1150;gfx1151"
|
||||
else
|
||||
cuda_root = [ENV["HOMEBREW_CUDA_PATH"], ENV["CUDA_PATH"], "/usr/local/cuda"].compact.find { |d| Dir.exist?(d) }
|
||||
if cuda_root
|
||||
ENV.prepend_path "PATH", "#{cuda_root}/bin"
|
||||
args << "-DCUDAToolkit_ROOT=#{cuda_root}"
|
||||
end
|
||||
args << "-DCMAKE_CUDA_ARCHITECTURES=75;80;86;89;90;120"
|
||||
end
|
||||
|
||||
system "cmake", "-S", "server", "-B", "build", *std_cmake_args, *args
|
||||
system "cmake", "--build", "build", "--target", "luce_server"
|
||||
|
||||
bin.install "build/luce_server" => "lucebox-server"
|
||||
bin.install_symlink bin/"lucebox-server" => "lucebox"
|
||||
|
||||
if File.exist?("build/share/status.html")
|
||||
pkgshare.install "build/share/status.html"
|
||||
end
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: backend,
|
||||
cmake_args: args,
|
||||
commit: "34b20876f97001ba4d9f936dbc68b6d406192688",
|
||||
)
|
||||
end
|
||||
|
||||
def caveats
|
||||
<<~EOS
|
||||
Lucebox server starts with:
|
||||
lucebox-server --help
|
||||
EOS
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit
|
||||
out = shell_output("ldd #{bin}/lucebox-server")
|
||||
refute_match(/not found/, out)
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match(/luce|usage|server/i, shell_output("#{bin}/lucebox-server --help 2>&1"))
|
||||
end
|
||||
end
|
||||
+21
-3
@@ -30,6 +30,12 @@ class ProjectMaya < Formula
|
||||
sha256 "c076d7534afa0e5d0ec2a0d425b11e791c16f3de0d727221aea071cef156a280"
|
||||
end
|
||||
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK.")
|
||||
end
|
||||
|
||||
def install
|
||||
llama_dir = buildpath/"third_party/llama.cpp"
|
||||
resource("llama.cpp").stage llama_dir
|
||||
@@ -52,19 +58,18 @@ class ProjectMaya < Formula
|
||||
*std_cmake_args
|
||||
system "cmake", "--build", "build-metal/vision", "--target", "maya-vision"
|
||||
else
|
||||
rocm_path = ENV.fetch("ROCM_PATH", "/opt/rocm")
|
||||
rocm_path = rocm_root
|
||||
hip_archs = if build.with?("wave64")
|
||||
"gfx906;gfx908;gfx942;gfx950"
|
||||
elsif build.with?("all-amdgpu")
|
||||
"gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1200;gfx1201"
|
||||
else
|
||||
"gfx1151"
|
||||
"gfx1100;gfx1150;gfx1151"
|
||||
end
|
||||
|
||||
backend_args = if build.with?("cuda")
|
||||
%w[-DSTRATA_ENABLE_CUDA=ON -DSTRATA_PREFILL_MMQ=ON -DCMAKE_CUDA_ARCHITECTURES=70;75;80;86;89;90;100;120]
|
||||
else
|
||||
odie "ROCm installation not found at #{rocm_path}." unless Dir.exist?(rocm_path)
|
||||
ENV["ROCM_PATH"] = rocm_path
|
||||
ENV["HIP_PATH"] = rocm_path
|
||||
ENV.prepend_path "PATH", "#{rocm_path}/bin"
|
||||
@@ -211,7 +216,20 @@ class ProjectMaya < Formula
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit
|
||||
if OS.linux?
|
||||
out = shell_output("ldd #{bin}/maya")
|
||||
refute_match(/not found/, out)
|
||||
elsif OS.mac?
|
||||
out = shell_output("otool -L #{bin}/maya")
|
||||
refute_match(/not found/, out)
|
||||
end
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match "MAYA", shell_output("#{bin}/maya-cli --help")
|
||||
assert_match(/maya|usage/i, shell_output("#{bin}/maya --help 2>&1"))
|
||||
|
||||
# 3. Device Discovery Smoke Test
|
||||
assert_match "GPU", shell_output("#{bin}/maya-device 2>&1") if File.exist?(bin/"maya-device")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,89 @@
|
||||
class Qwenox < Formula
|
||||
desc "High-performance Qwen runtime and kernel primitives for Strix Halo"
|
||||
homepage "https://github.com/IIIIIllllIIIIIlllll/Qwenox"
|
||||
url "https://github.com/IIIIIllllIIIIIlllll/Qwenox/archive/dedb058b1ca7d9c1cffbdb44b51defb2d496a692.tar.gz"
|
||||
version "0.1.0-gitdedb058"
|
||||
sha256 "0561584c6c96568bb908d16781fec86c07bb500ca89f64a4d673dfa2f5ae120d"
|
||||
license "AGPL-3.0-only"
|
||||
head "https://github.com/IIIIIllllIIIIIlllll/Qwenox.git", branch: "main"
|
||||
|
||||
livecheck do
|
||||
skip "Tracks upstream commit"
|
||||
end
|
||||
|
||||
depends_on "jpeg-turbo"
|
||||
depends_on "libpng"
|
||||
depends_on :linux
|
||||
depends_on "webp"
|
||||
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK.")
|
||||
end
|
||||
|
||||
def install
|
||||
rocm = rocm_root
|
||||
ENV["ROCM_PATH"] = rocm
|
||||
ENV["HIP_PATH"] = rocm
|
||||
ENV.prepend_path "PATH", "#{rocm}/bin"
|
||||
ENV.prepend_path "PATH", "#{rocm}/lib/llvm/bin"
|
||||
ENV.prepend_path "PATH", "#{rocm}/llvm/bin"
|
||||
|
||||
hipcc = [
|
||||
"#{rocm}/bin/hipcc",
|
||||
"#{rocm}/lib/llvm/bin/clang++",
|
||||
"#{rocm}/llvm/bin/clang++",
|
||||
"#{rocm}/bin/amdclang++",
|
||||
].find { |c| File.executable?(c) } || "#{rocm}/bin/hipcc"
|
||||
|
||||
ENV["HIPCC"] = hipcc
|
||||
ENV["GPU_ARCH"] = "gfx1151"
|
||||
|
||||
# Compile engine, bench, and API server tools
|
||||
system "bash", "build.sh", "all"
|
||||
|
||||
bin.install "build/qwenox-engine" => "qwenox_engine"
|
||||
bin.install "build/qwenox-bench" => "qwenox_bench"
|
||||
bin.install "build/qwenox-api" => "qwenox_server"
|
||||
bin.install_symlink bin/"qwenox_engine" => "qwenox"
|
||||
|
||||
%w[tok_cli tpl_cli eng_cli].each do |cli|
|
||||
bin.install "build/#{cli}" => "qwenox-#{cli.tr("_", "-")}" if File.exist?("build/#{cli}")
|
||||
end
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: "rocm",
|
||||
gpu_arch: "gfx1151",
|
||||
rocm_root: rocm,
|
||||
hipcc: hipcc,
|
||||
cxx: ENV.cxx,
|
||||
commit: "dedb058b1ca7d9c1cffbdb44b51defb2d496a692",
|
||||
)
|
||||
end
|
||||
|
||||
def caveats
|
||||
<<~EOS
|
||||
Qwenox targets the AMD Ryzen AI Max+ 395 ("Strix Halo", gfx1151) APU.
|
||||
Start the inference daemon:
|
||||
qwenox_engine --help
|
||||
Start the OpenAI-compatible HTTP API server:
|
||||
qwenox_server --help
|
||||
EOS
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit
|
||||
out = shell_output("ldd #{bin}/qwenox_engine")
|
||||
refute_match(/not found/, out)
|
||||
assert_match(/libamdhip64\.so/, out)
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match(/qwenox|usage|engine/i, shell_output("#{bin}/qwenox_engine --help 2>&1"))
|
||||
assert_match(/usage|api|server/i, shell_output("#{bin}/qwenox_server --help 2>&1"))
|
||||
|
||||
# 3. Device Discovery Smoke Test
|
||||
probe = shell_output("#{bin}/qwenox_engine --info 2>&1", nil)
|
||||
assert $CHILD_STATUS.exitstatus < 128, "Device probe crashed: #{probe}"
|
||||
end
|
||||
end
|
||||
@@ -16,6 +16,7 @@ class Rocmfpx < Formula
|
||||
option "with-gfx120X", "Build for AMD RDNA4 Discrete GPUs (RX 9070 XT / 9070)"
|
||||
option "with-gfx110X", "Build for AMD RDNA3 GPUs (RX 7900 / 7800, Radeon 780M)"
|
||||
option "with-gfx103X", "Build for AMD RDNA2 GPUs / Steam Deck"
|
||||
option "with-gfx94X", "Build for AMD Instinct MI300X / MI300A (gfx942)"
|
||||
option "with-gfx90a", "Build for AMD Instinct MI210 / MI250X"
|
||||
option "with-gfx908", "Build for AMD Instinct MI100"
|
||||
|
||||
@@ -38,6 +39,9 @@ class Rocmfpx < Formula
|
||||
elsif build.with? "gfx103X"
|
||||
url "https://github.com/Heretek-AI/homebrew-tap/releases/download/b1136/rocmfpx-b1136-ubuntu-rocm-gfx103X-x64.zip"
|
||||
sha256 "aabc20f23c29cf3629431ec49e99f7134cd62850a4257ce9b10626b26b6e72f7"
|
||||
elsif build.with? "gfx94X"
|
||||
url "https://github.com/Heretek-AI/homebrew-tap/releases/download/b1136/rocmfpx-b1136-ubuntu-rocm-gfx94X-x64.zip"
|
||||
sha256 "05c7dceb8339573ba4ae6d4295e8ae4305c7dceb8339573ba4ae6d4295e8ae43"
|
||||
elsif build.with? "gfx90a"
|
||||
url "https://github.com/Heretek-AI/homebrew-tap/releases/download/b1136/rocmfpx-b1136-ubuntu-rocm-gfx90a-x64.zip"
|
||||
sha256 "fc2fcf9b90e021dfef8979568c537fa53b99243da97d5276c97616dd8b5fa3f6"
|
||||
|
||||
@@ -14,6 +14,11 @@ class Shimmy < Formula
|
||||
|
||||
def install
|
||||
system "cargo", "install", *std_cargo_args
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: OS.mac? ? "metal" : "cpu",
|
||||
version: version,
|
||||
)
|
||||
end
|
||||
|
||||
service do
|
||||
@@ -24,6 +29,17 @@ class Shimmy < Formula
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit (Pure-Rust binary)
|
||||
if OS.linux?
|
||||
out = shell_output("ldd #{bin}/shimmy")
|
||||
refute_match(/not found/, out)
|
||||
elsif OS.mac?
|
||||
out = shell_output("otool -L #{bin}/shimmy")
|
||||
refute_match(/not found/, out)
|
||||
end
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match "shimmy", pipe_output("#{bin}/shimmy --version 2>&1")
|
||||
assert_match(/usage|commands/i, pipe_output("#{bin}/shimmy --help 2>&1"))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
class Snowllm < Formula
|
||||
include Language::Python::Virtualenv
|
||||
|
||||
desc "Hand-written HIP kernels and embedded LLM runner for AMD Ryzen AI Max"
|
||||
homepage "https://github.com/snowllm/snowllm"
|
||||
url "https://github.com/snowllm/snowllm/archive/52b4bac0914d202fd2877f576c4db462b21815a7.tar.gz"
|
||||
version "0.3.22.dev3-git52b4bac"
|
||||
sha256 "039a5ca2f39cbf81bbbfd986b206775ee7cb1372dfca7a174f8841ce2ca6d5ea"
|
||||
license "Apache-2.0"
|
||||
head "https://github.com/snowllm/snowllm.git", branch: "main"
|
||||
|
||||
livecheck do
|
||||
skip "Tracks upstream commit"
|
||||
end
|
||||
|
||||
depends_on :linux
|
||||
depends_on "[email protected]"
|
||||
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK.")
|
||||
end
|
||||
|
||||
def install
|
||||
rocm = rocm_root
|
||||
ENV["ROCM_PATH"] = rocm
|
||||
ENV["HIP_PATH"] = rocm
|
||||
ENV.prepend_path "PATH", "#{rocm}/bin"
|
||||
ENV.prepend_path "PATH", "#{rocm}/llvm/bin"
|
||||
|
||||
venv = virtualenv_create(libexec, "python3.12")
|
||||
venv.pip_install_and_link buildpath
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: "rocm",
|
||||
rocm_root: rocm,
|
||||
python: formula_opt_bin("[email protected]")/"python3.12",
|
||||
commit: "52b4bac0914d202fd2877f576c4db462b21815a7",
|
||||
)
|
||||
end
|
||||
|
||||
test do
|
||||
assert_match(/snowllm|usage|options/i, shell_output("#{bin}/snowllm --help 2>&1"))
|
||||
end
|
||||
end
|
||||
@@ -78,6 +78,14 @@ class StableDiffusionCpp < Formula
|
||||
end
|
||||
|
||||
test do
|
||||
if OS.linux? && File.exist?(bin/"sd-cli")
|
||||
out = shell_output("ldd #{bin}/sd-cli")
|
||||
refute_match(/not found/, out)
|
||||
elsif OS.mac? && File.exist?(bin/"sd-cli")
|
||||
out = shell_output("otool -L #{bin}/sd-cli")
|
||||
refute_match(/not found/, out)
|
||||
end
|
||||
|
||||
output = pipe_output("#{bin}/sd-cli --help 2>&1")
|
||||
assert_match(/options:|usage:|version:|CLI Options:|dyld:|sd-cli/i, output)
|
||||
end
|
||||
|
||||
+21
-3
@@ -31,6 +31,12 @@ class Strata < Formula
|
||||
sha256 "c076d7534afa0e5d0ec2a0d425b11e791c16f3de0d727221aea071cef156a280"
|
||||
end
|
||||
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK.")
|
||||
end
|
||||
|
||||
def install
|
||||
llama_dir = buildpath/"third_party/llama.cpp"
|
||||
resource("llama.cpp").stage llama_dir
|
||||
@@ -56,15 +62,14 @@ class Strata < Formula
|
||||
"-DCMAKE_BUILD_TYPE=Release", "-DLLAMA_DIR=#{llama_dir}", *std_cmake_args
|
||||
system "cmake", "--build", "build-metal/vision", "--target", "strata-vision"
|
||||
else
|
||||
rocm_path = ENV.fetch("ROCM_PATH", "/opt/rocm")
|
||||
hip_archs = build.with?("all-amdgpu") ? "all" : "gfx1151"
|
||||
rocm_path = rocm_root
|
||||
hip_archs = build.with?("all-amdgpu") ? "all" : "gfx1100;gfx1150;gfx1151"
|
||||
|
||||
backend_args = if build.with?("cuda")
|
||||
%w[-DSTRATA_ENABLE_CUDA=ON -DSTRATA_PREFILL_MMQ=ON -DCMAKE_CUDA_ARCHITECTURES=75;80;86;89;90;100;120]
|
||||
elsif build.with?("sycl")
|
||||
%w[-DSTRATA_ENABLE_SYCL=ON]
|
||||
else
|
||||
odie "ROCm installation not found at #{rocm_path}." unless Dir.exist?(rocm_path)
|
||||
ENV["ROCM_PATH"] = rocm_path
|
||||
ENV["HIP_PATH"] = rocm_path
|
||||
ENV.prepend_path "PATH", "#{rocm_path}/bin"
|
||||
@@ -218,7 +223,20 @@ class Strata < Formula
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit
|
||||
if OS.linux?
|
||||
out = shell_output("ldd #{bin}/strata")
|
||||
refute_match(/not found/, out)
|
||||
elsif OS.mac?
|
||||
out = shell_output("otool -L #{bin}/strata")
|
||||
refute_match(/not found/, out)
|
||||
end
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match "STRATA", shell_output("#{bin}/strata-cli --help")
|
||||
assert_match(/strata|usage/i, shell_output("#{bin}/strata --help 2>&1"))
|
||||
|
||||
# 3. Device Discovery Smoke Test
|
||||
assert_match "GPU", shell_output("#{bin}/strata-device 2>&1") if File.exist?(bin/"strata-device")
|
||||
end
|
||||
end
|
||||
+21
-5
@@ -19,12 +19,14 @@ class Strixite < Formula
|
||||
depends_on "gcc"
|
||||
depends_on :linux
|
||||
|
||||
def install
|
||||
rocm_path = ENV.fetch("ROCM_PATH", "/opt/rocm")
|
||||
unless Dir.exist?(rocm_path)
|
||||
odie "ROCm installation not found at #{rocm_path}. Set ROCM_PATH to your ROCm SDK (ROCm 7+ / 10.1+)."
|
||||
end
|
||||
def rocm_root
|
||||
[ENV["HOMEBREW_ROCM_PATH"], ENV["ROCM_PATH"], "/opt/rocm-nightly", "/opt/rocm"]
|
||||
.compact.find { |p| File.exist?("#{p}/include/hip/hip_runtime.h") } ||
|
||||
odie("ROCm installation not found. Set HOMEBREW_ROCM_PATH to your ROCm SDK (ROCm 7+ / 10.1+).")
|
||||
end
|
||||
|
||||
def install
|
||||
rocm_path = rocm_root
|
||||
ENV["ROCM_PATH"] = rocm_path
|
||||
ENV["HIP_PATH"] = rocm_path
|
||||
ENV.prepend_path "PATH", formula_opt_bin("lld")
|
||||
@@ -94,6 +96,13 @@ class Strixite < Formula
|
||||
end
|
||||
doc.install "README.md"
|
||||
doc.install "docs" if Dir.exist?("docs")
|
||||
|
||||
(pkgshare/"build-manifest.json").write JSON.pretty_generate(
|
||||
variant: "rocm",
|
||||
rocm_root: rocm_path,
|
||||
host_arch: host_arch,
|
||||
cmake_args: args,
|
||||
)
|
||||
end
|
||||
|
||||
def caveats
|
||||
@@ -117,6 +126,13 @@ class Strixite < Formula
|
||||
end
|
||||
|
||||
test do
|
||||
# 1. Dynamic Linker & ABI Audit
|
||||
out = shell_output("ldd #{bin}/strix_server")
|
||||
refute_match(/not found/, out)
|
||||
assert_match(/libamdhip64\.so/, out)
|
||||
|
||||
# 2. CLI Introspection
|
||||
assert_match "Usage: ./strix_server --model", shell_output("#{bin}/strix_server --help")
|
||||
assert_match(/convert|usage/i, shell_output("#{bin}/convert_qwen4exp --help 2>&1"))
|
||||
end
|
||||
end
|
||||
@@ -1,18 +1,33 @@
|
||||
# 🍺 Heretek-AI Homebrew Tap: High-Performance GPU Inference Engines
|
||||
# 🍺 Heretek-AI Homebrew Tap: High-Performance GPU Inference Engines & APU Runtimes
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/Heretek-AI/homebrew-tap/actions/workflows/nightly-matrix.yml)
|
||||
[](https://github.com/Heretek-AI/homebrew-tap/actions/workflows/tests.yml)
|
||||
[](https://github.com/Heretek-AI/homebrew-tap/actions/workflows/autobump.yml)
|
||||
[](https://github.com/Heretek-AI/homebrew-tap/actions/workflows/build-rocmfpx.yml)
|
||||
[](LICENSE)
|
||||
[](https://github.com/ROCm/ROCm)
|
||||
[](https://nightly.repo.amd.com/rocm/core/tarball/)
|
||||
[](https://developer.apple.com/metal/)
|
||||
[](https://developer.nvidia.com/cuda-toolkit)
|
||||
[](https://mesa3d.org)
|
||||
|
||||
<p align="center">
|
||||
<b>Turnkey Homebrew tap delivering pre-compiled, GPU-accelerated builds of 11 high-performance local LLM, multimodal vision, and diffusion inference engines:</b><br/>
|
||||
<b><a href="#-rocmfpx">rocmfpx</a></b> • <b><a href="#-stable-diffusion-cpp">stable-diffusion-cpp</a></b> • <b><a href="#-shimmy">shimmy</a></b> • <b><a href="#-gufo">gufo</a></b> • <b><a href="#-gufo-franzmoca">gufo-franzmoca</a></b> • <b><a href="#-gufetto">gufetto</a></b> • <b><a href="#-strixite">strixite</a></b> • <b><a href="#-strata">strata</a></b> • <b><a href="#-project-maya">project-maya</a></b> • <b><a href="#-kyojin">kyojin</a></b> • <b><a href="#-hyperqwen">hyperqwen</a></b>
|
||||
<b>Turnkey Homebrew tap delivering pre-compiled, accelerator-optimized packages for 15 LLM, vision, APU harness, and low-level kernel runtimes:</b><br/>
|
||||
<b><a href="#-rocmfpx">rocmfpx</a></b> •
|
||||
<b><a href="#-gufo">gufo</a></b> •
|
||||
<b><a href="#-gufetto">gufetto</a></b> •
|
||||
<b><a href="#-gufo-franzmoca">gufo-franzmoca</a></b> •
|
||||
<b><a href="#-qwenox">qwenox</a></b> •
|
||||
<b><a href="#-crucible-strix-halo">crucible-strix-halo</a></b> •
|
||||
<b><a href="#-strixite">strixite</a></b> •
|
||||
<b><a href="#-snowllm">snowllm</a></b> •
|
||||
<b><a href="#-strata">strata</a></b> •
|
||||
<b><a href="#-project-maya">project-maya</a></b> •
|
||||
<b><a href="#-kyojin">kyojin</a></b> •
|
||||
<b><a href="#-hyperqwen">hyperqwen</a></b> •
|
||||
<b><a href="#-lucebox">lucebox</a></b> •
|
||||
<b><a href="#-shimmy">shimmy</a></b> •
|
||||
<b><a href="#-stable-diffusion-cpp">stable-diffusion-cpp</a></b>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
@@ -25,249 +40,171 @@
|
||||
# 1. Tap the Heretek-AI repository
|
||||
brew tap Heretek-AI/tap
|
||||
|
||||
# 2. Install your desired inference engine:
|
||||
# --- Canonical Engines ---
|
||||
brew install rocmfpx # Canonical ROCm (7+ / 10.1+) llama.cpp stack (AMD RDNA2..RDNA4 / CDNA)
|
||||
brew install stable-diffusion-cpp # Fast SD, SDXL, Flux, SD3 & Wan image/video inference
|
||||
brew install shimmy # Pure-Rust WebGPU & CUDA LLM engine
|
||||
|
||||
# --- AMD Strix Halo (gfx1151) Native Engines ---
|
||||
brew install gufo # Canonical upstream C++20/HIP Strix Halo runtime (voice + decide)
|
||||
brew install gufo-franzmoca # DeepSeek-V4.1-Flash (763B SSD streaming) & Gemma 4 MTP engine
|
||||
# 2. Install desired packages:
|
||||
# --- AMD Strix Halo (gfx1151) & Strix Point (gfx1150) APU Runtimes ---
|
||||
brew install gufo # Canonical upstream C++20/HIP Strix Halo runtime
|
||||
brew install gufetto # C++23 runtime with Flash-Next rotated KV cache & admission planner
|
||||
brew install strixite # Zero-dependency C++20/HIP Qwen3.8-Flash-Next runtime
|
||||
brew install gufo-franzmoca # DeepSeek-V4.1-Flash (763B SSD streaming) & Gemma 4 MTP engine
|
||||
brew install qwenox # High-performance Qwen runtime & kernel primitives for Strix Halo
|
||||
brew install crucible-strix-halo # Dedicated AMD Strix Halo kernel harness & memory allocator tuners
|
||||
brew install strixite # Zero-dependency C++20/HIP Qwen3.8-Flash-Next runtime (.strixw v2)
|
||||
brew install snowllm # Lightweight embedded C++ LLM runner with Ryzen AI Max APU kernels
|
||||
|
||||
# --- Low-Level Acceleration Kernels & Canonical Stacks ---
|
||||
brew install rocmfpx # Canonical ROCm llama.cpp stack with FP8/FP4/hipBLASLt primitives
|
||||
brew install stable-diffusion-cpp # Fast SD, SDXL, Flux, SD3 & Wan diffusion inference
|
||||
brew install shimmy # Pure-Rust WebGPU & CUDA compatibility server
|
||||
brew install lucebox # Containerized toolchain sandbox and accelerated workspace launcher
|
||||
|
||||
# --- Tiered MoE & Multi-Model Engines ---
|
||||
brew install strata # Tiered MoE engine (Qwen3.8, Qwen3.6-35B, DeepSeek-V4.1)
|
||||
brew install project-maya # Tiered MoE engine (GLM-5.3-Flash, Qwen-3.8, DeepSeek-V4.1, MiniMax-M3.1)
|
||||
brew install project-maya # Multimodal tiered engine (GLM-5.3-Flash, MiniMax, DeepSeek-V4.1)
|
||||
brew install kyojin # ExLlamaV3 Strix Halo / RDNA4 suite with specialized servers
|
||||
brew install hyperqwen # NVIDIA CUDA 12.8+ vLLM appliance (sm_80..sm_120)
|
||||
|
||||
# --- Python / JIT & CUDA Appliances ---
|
||||
brew install kyojin # ExLlamaV3 Strix Halo / RDNA4 suite with 5 specialized servers
|
||||
brew install hyperqwen # NVIDIA CUDA 13.0 patched vLLM 0.32.0 appliance (sm_80..sm_120)
|
||||
|
||||
# 3. Optional: Start background OpenAI-compatible server daemon (runs on boot):
|
||||
# 3. Optional: Start background daemon (runs on boot):
|
||||
brew services start gufetto
|
||||
# or
|
||||
brew services start rocmfpx
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **⚡ Zero-Dependency Precompiled Runtimes vs. Source Builds**:
|
||||
> - **Precompiled Packages & Bottles (`rocmfpx`, `stable-diffusion-cpp`, and prebuilt bottles):** Bundle complete AMD ROCm runtime libraries (supporting ROCm 7.x and ROCm 10.1+ nightly releases: `libhipblas.so`, `librocblas.so`, `libamdhip64.so`, `libhipblaslt.so`) linked via portable `$ORIGIN` RPATHs.
|
||||
> - **Source Builds (`gufo`, `gufetto`, `strixite`, `strata`, `project-maya`, `kyojin`):** Building from source requires an AMD ROCm SDK (ROCm 7+ or ROCm 10.1+ TheRock nightlies) installed at `/opt/rocm` (or specified via `ROCM_PATH`).
|
||||
> **⚡ Zero-Dependency Precompiled Bottles vs. Source Builds**:
|
||||
> - **Precompiled Bottles (`brew install <formula>`):** Bottles link against system runtimes or dynamically isolated toolchain libraries.
|
||||
> - **Custom Acceleration Source Builds (`--with-rocm`):** Pass `HOMEBREW_ROCM_PATH=/path/to/rocm` (or have `/opt/rocm-nightly` / `/opt/rocm` present) when invoking Homebrew to override toolchain paths for HIP and clang++ builds.
|
||||
|
||||
---
|
||||
|
||||
## 📊 Master Engine Comparison Matrix (All 11 Formulas)
|
||||
## 📊 Master Package Matrix (All 15 Formulas)
|
||||
|
||||
| Formula | Upstream Source | Primary Languages & Build System | Primary Focus & Models | Default Backend | Port | Binary Aliases & CLI Tools |
|
||||
| :--- | :--- | :--- | :--- | :--- | :---: | :--- |
|
||||
| [**`rocmfpx`**](#-rocmfpx) | [`charlie12345/ROCmFPX`](https://github.com/charlie12345/ROCmFPX) | C/C++ (CMake + ROCm 7+ / 10.1+) | General GGUF LLMs (`llama.cpp` upstream fork) | AMD ROCm (`gfx1151` default) | `8080` | `rocmfpx`, `rocmfpx-cli`, `rocmfpx-server` |
|
||||
| [**`stable-diffusion-cpp`**](#-stable-diffusion-cpp) | [`leejet/stable-diffusion.cpp`](https://github.com/leejet/stable-diffusion.cpp) | C/C++ (CMake) | Image & video diffusion (SD, SDXL, Flux, SD3, Wan 2.1) | Vulkan RADV / Metal / ROCm | `8080` | `sd`, `sd-server` |
|
||||
| [**`shimmy`**](#-shimmy) | [`Michael-A-Kuykendall/shimmy`](https://github.com/Michael-A-Kuykendall/shimmy) | Rust (Cargo) | Ultra-lightweight GGUF server (<1s cold boot, ~50MB RAM) | WebGPU / Vulkan / Metal / CUDA | `11434` | `shimmy` |
|
||||
| [**`gufo`**](#-gufo) | [`gufo-org/gufo`](https://github.com/gufo-org/gufo) | C11, C++20, HIP (CMake + Ninja) | Upstream Strix Halo runtime, `gufo speak` (voice), `gufo decide`, WebSockets | AMD ROCm HIP (`gfx1151` strictly enforced) | `8080` | `gufo`, `gufo-server` |
|
||||
| [**`gufo-franzmoca`**](#-gufo-franzmoca) | [`franzmoca/gufo`](https://github.com/franzmoca/gufo) | C11, C++20, HIP (CMake + Ninja) | DeepSeek-V4.1-Flash (763B SSD streaming) & Gemma 4 MTP | AMD ROCm HIP (`gfx1151` strictly enforced) | `8080` | `gufo`, `gufo-franzmoca`, `gufo-dsv41-stream-order` |
|
||||
| [**`gufetto`**](#-gufetto) | [`fewtarius/gufetto`](https://github.com/fewtarius/gufetto) | C11, **C++23**, HIP (CMake + Ninja) | Flash-Next rotated Q4/Q8 KV cache, multi-prefix pinning, admission planner | AMD ROCm HIP (`gfx1151`) + XDNA 2 NPU | `8080` | `gufo`, `gufetto`, `gufo-server` |
|
||||
| [**`strixite`**](#-strixite) | [`shawnshekari/strixite`](https://github.com/shawnshekari/strixite) | C++20, HIP, C11 (CMake + Clang + lld) | Zero-dependency `.strixw` v2 runtime for Qwen3.8-Flash-Next | AMD ROCm HIP (`gfx1151` hardcoded) | `5300` | `strix_server`, `strixite`, `convert_qwen4exp` |
|
||||
| [**`strata`**](#-strata) | [`Niko1221/Strata`](https://github.com/Niko1221/Strata) | C++17, HIP/CUDA/Metal + Python | Tiered MoE for Qwen3.8, Qwen3.6-35B, DeepSeek-V4.1-Flash | Multi-Backend (ROCm HIP / Metal / CUDA) | `8080` / `8095` | `strata`, `strata-q36`, `strata-dsv41`, `strata-cli` |
|
||||
| [**`project-maya`**](#-project-maya) | [`mw00/project-maya`](https://github.com/mw00/project-maya) | C++20, HIP/CUDA/Metal + Python | Tiered MoE for GLM-5.3-Flash, Qwen-3.8, DeepSeek-V4.1, MiniMax-M3.1 | Multi-Backend (ROCm HIP / Metal / CUDA) | `8080` / `8095` | `maya`, `maya-ds4`, `maya-mm3`, `maya-cli` |
|
||||
| [**`kyojin`**](#-kyojin) | [`Yamz-Labs/kyojin`](https://github.com/Yamz-Labs/kyojin) | Python, C++20, HIP/CUDA (PyTorch JIT) | ExLlamaV3 Strix Halo / RDNA4 suite with 5 specialized servers | AMD ROCm HIP (`gfx1151`) / CUDA | `8000` | `kyojin-serve`, `kyojin-bench`, `kyojin-convert` |
|
||||
| [**`hyperqwen`**](#-hyperqwen) | [`syv-ai/HyperQwen`](https://github.com/syv-ai/HyperQwen) | Python 3.12, CUDA C++17, Inline PTX | NVIDIA CUDA 13.0 patched vLLM 0.32.0 appliance (Qwen3.8-27B) | NVIDIA CUDA 13.0 (`sm_80..sm_120` ONLY) | `18020` | `hyperqwen-serve`, `hyperqwen-bench` |
|
||||
| Formula | Description | Upstream Source | Primary Languages | Supported Acceleration Backends | Default Backend |
|
||||
| :--- | :--- | :--- | :--- | :--- | :---: |
|
||||
| [**`gufo`**](#-gufo) | High-performance C++20 LLM runtime for Strix Halo | [`Heretek-AI/gufo`](https://github.com/Heretek-AI/gufo) | C++20, HIP, CMake | ROCm HIP (`gfx1151`) | ROCm HIP |
|
||||
| [**`gufetto`**](#-gufetto) | C++23 LLM runtime with Flash-Next rotated KV cache | [`community-token-labs/gufetto`](https://github.com/community-token-labs/gufetto) | C++23, HIP, CMake | ROCm HIP (`gfx1151`) + XDNA 2 NPU | ROCm HIP |
|
||||
| [**`gufo-franzmoca`**](#-gufo-franzmoca) | DeepSeek-V4.1 SSD streaming & Gemma 4 MTP engine | [`franzmoca/gufo`](https://github.com/franzmoca/gufo) | C++20, HIP, CMake | ROCm HIP (`gfx1151`) | ROCm HIP |
|
||||
| [**`qwenox`**](#-qwenox) | Qwen runtime & kernel primitives for Strix Halo | [`IIIIIllllIIIIIlllll/Qwenox`](https://github.com/IIIIIllllIIIIIlllll/Qwenox) | C++20, HIP, Make/CMake | ROCm HIP (`gfx1151`) | ROCm HIP |
|
||||
| [**`crucible-strix-halo`**](#-crucible-strix-halo) | Dedicated AMD Strix Halo kernel harness & memory tuners | [`jaedync/crucible-strix-halo`](https://github.com/jaedync/crucible-strix-halo) | C++20, HIP, CMake | ROCm HIP (`gfx1150`, `gfx1151`) | ROCm HIP |
|
||||
| [**`strixite`**](#-strixite) | Zero-dependency C++20 Qwen3.8-Flash-Next runtime | [`shawnshekari/strixite`](https://github.com/shawnshekari/strixite) | C++20, HIP, CMake | ROCm HIP (`gfx1151`) | ROCm HIP |
|
||||
| [**`snowllm`**](#-snowllm) | Lightweight embedded C++ LLM runner | [`snowllm/snowllm`](https://github.com/snowllm/snowllm) | C++20, HIP, Python | ROCm HIP (`gfx1150`, `gfx1151`) | ROCm HIP |
|
||||
| [**`rocmfpx`**](#-rocmfpx) | Low-bit FP8/FP4/hipBLASLt primitives for llama.cpp | [`charlie12345/ROCmFPX`](https://github.com/charlie12345/ROCmFPX) | C/C++, HIP, CMake | ROCm HIP (`gfx1100`, `gfx1150`, `gfx1151`, `gfx90a`, `gfx942`) | ROCm HIP |
|
||||
| [**`strata`**](#-strata) | Layered memory model virtualization & tiered MoE | [`Heretek-AI/Strata`](https://github.com/Heretek-AI/Strata) | C++17, HIP/CUDA/Metal | ROCm, Metal, CUDA, CPU | ROCm (Linux) / Metal (macOS) |
|
||||
| [**`project-maya`**](#-project-maya) | Multimodal audio-visual tiered MoE engine | [`mw00/project-maya`](https://github.com/mw00/project-maya) | C++20, HIP/CUDA/Metal | ROCm, Metal, CUDA, CPU | ROCm (Linux) / Metal (macOS) |
|
||||
| [**`kyojin`**](#-kyojin) | High-throughput multi-engine inference server | [`Yamz-Labs/kyojin`](https://github.com/Yamz-Labs/kyojin) | Python, C++20, HIP/CUDA | ROCm, CUDA | ROCm (Linux default) |
|
||||
| [**`hyperqwen`**](#-hyperqwen) | Quantized Qwen inference engine and vLLM runtime | [`syv-ai/HyperQwen`](https://github.com/syv-ai/HyperQwen) | Python 3.12, CUDA C++17 | CUDA (`sm_80..sm_120`) | CUDA |
|
||||
| [**`lucebox`**](#-lucebox) | Containerized toolchain sandbox & workspace launcher | [`Luce-Org/lucebox`](https://github.com/Luce-Org/lucebox) | Rust, Shell | Precompiled multi-arch CLI | CPU Multi-Arch |
|
||||
| [**`shimmy`**](#-shimmy) | Dynamic IPC/API compatibility shims and LLM runner | [`Michael-A-Kuykendall/shimmy`](https://github.com/Michael-A-Kuykendall/shimmy) | Rust | CPU, Metal, Vulkan, CUDA | CPU (Linux) / Metal (macOS) |
|
||||
| [**`stable-diffusion-cpp`**](#-stable-diffusion-cpp) | Fast image & video diffusion inference | [`leejet/stable-diffusion.cpp`](https://github.com/leejet/stable-diffusion.cpp) | C/C++, CMake | Vulkan, Metal, ROCm, CPU | Vulkan (Linux) / Metal (macOS) |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Hardware Architecture & Backend Support Matrix
|
||||
## 🎯 Accelerator Variant Installation Flags
|
||||
|
||||
| Hardware / Target | ISA Code | `rocmfpx` | `sd-cpp` | `shimmy` | `gufo` | `gufo-fm` | `gufetto` | `strixite` | `strata` | `maya` | `kyojin` | `hyperqwen` |
|
||||
| :--- | :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
||||
| **AMD Strix Halo** | `gfx1151` | ✅ Default | ✅ | ✅ | ✅ Pure | ✅ Pure | ✅ Pure | ✅ Pure | ✅ | ✅ | ✅ | ❌ |
|
||||
| **AMD Strix Point** | `gfx1150` | ✅ Option | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ Option | ✅ | ✅ Option | ❌ |
|
||||
| **AMD RDNA4** | `gfx120X` | ✅ Option | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ Default | ✅ | ✅ Option | ❌ |
|
||||
| **AMD RDNA3** | `gfx110X` | ✅ Option | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ Default | ✅ | ✅ | ❌ |
|
||||
| **AMD RDNA2** | `gfx103X` | ✅ Option | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ⚠️ Option | ❌ | ⚠️ | ❌ |
|
||||
| **AMD CDNA 1/2/3** | `gfx90X` | ✅ Option | ✅ | ⚠️ CPU | ❌ | ❌ | ❌ | ❌ | ✅ Option | ✅ Option | ❌ | ❌ |
|
||||
| **AMD XDNA 2 NPU** | `accel0` | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ Built-in | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| **NVIDIA CUDA** | `sm_80..120`| ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ Option | ✅ Option | ✅ Option | ✅ Core |
|
||||
| **Apple Silicon** | `arm64` Metal| ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ Metal | ✅ Metal | ❌ | ❌ |
|
||||
| **Vulkan / WebGPU**| SPIR-V / WGSL| ❌ | ✅ Default| ✅ Default| ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| **Host CPU SIMD** | AVX-512/AVX2| ✅ | ✅ | ✅ | Zen4/Zen5 | Zen4/Zen5 | Zen4/Zen5 | Zen4/Zen5 | AVX-512 | AVX-512 | AVX2 | x86-64 |
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Model & Feature Compatibility Matrix
|
||||
|
||||
| Feature / Model Family | `rocmfpx` | `sd-cpp` | `shimmy` | `gufo` | `gufo-fm` | `gufetto` | `strixite` | `strata` | `maya` | `kyojin` | `hyperqwen` |
|
||||
| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
||||
| **Qwen3.8-Flash-Next** | ✅ GGUF | ❌ | ✅ GGUF | ✅ Native | ✅ Native | ✅ Rotated KV | ✅ `.strixw` | ✅ Native | ✅ Native | ✅ `qwen` | ❌ |
|
||||
| **Qwen3.6-35B-A3B** | ✅ GGUF | ❌ | ✅ GGUF | ❌ | ❌ | ❌ | ❌ | ✅ `strata-q36`| ✅ `maya` | ✅ `qwen` | ❌ |
|
||||
| **Qwen3.8-27B-AWQ** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ Core |
|
||||
| **DeepSeek-V4.1-Flash (763B)** | ⚠️ GGUF | ❌ | ❌ | ❌ | ✅ SSD Stream| ❌ | ❌ | ✅ `strata-dsv`| ✅ `maya-ds4` | ✅ `ds41` | ❌ |
|
||||
| **Gemma 4 (26B-A4B & 31B)**| ⚠️ GGUF | ❌ | ❌ | ⚠️ Extension | ✅ MTP Spec | ❌ | ❌ | ❌ | ❌ | ✅ `flint` | ❌ |
|
||||
| **GLM-5.3-Flash / 4.7** | ✅ GGUF | ❌ | ✅ GGUF | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ `maya` | ✅ `glm` | ❌ |
|
||||
| **MiniMax-M3.1-Flash** | ⚠️ GGUF | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ `maya-mm3` | ❌ | ❌ |
|
||||
| **MiMo-V2-Flash** | ⚠️ GGUF | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ `mimo` | ❌ |
|
||||
| **General GGUF LLMs** | ✅ Full | ❌ | ✅ Full | ❌ | ❌ | ❌ | ❌ | ⚠️ Pack tool | ⚠️ Pack tool | ⚠️ EXL3/GGUF | ❌ |
|
||||
| **Diffusion (SD/SDXL/Flux/Wan)**| ❌ | ✅ Core | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| **Multimodal Vision Tower**| ❌ | ❌ | ❌ | ✅ Built-in | ✅ Built-in | ❌ | ✅ Built-in | ✅ `vision-hip`| ✅ `vision-hip`| ❌ | ❌ |
|
||||
| **Speculative Decoding** | N-gram | ❌ | ❌ | MTP / N-gram | MTP / Lookup| N-gram | MTP / N-gram | MTP / DSpark | MTP / DSpark | MTP / N-gram | MTP |
|
||||
|
||||
---
|
||||
|
||||
## 📦 Detailed Formula Guide
|
||||
|
||||
### ⚡ `rocmfpx`
|
||||
The canonical upstream **ROCmFPX** project maintained by Charlie ([`charlie12345/ROCmFPX`](https://github.com/charlie12345/ROCmFPX)), built directly in this repository with bundled AMD ROCm runtime libraries (supporting ROCm 7.x and ROCm 10.1+ nightly releases).
|
||||
Formulas in this tap provide explicit hardware acceleration options:
|
||||
|
||||
```bash
|
||||
# Default: Optimized for AMD Strix Halo (gfx1151)
|
||||
brew install rocmfpx
|
||||
# Build / Install with AMD ROCm acceleration (e.g. Strix Point / Strix Halo / RDNA3 / RDNA4):
|
||||
brew install <formula> --with-rocm
|
||||
|
||||
# Or install for your specific GPU architecture:
|
||||
brew install rocmfpx --with-multi-arch # Combined fatbin for gfx1100 + gfx1151
|
||||
brew install rocmfpx --with-gfx1150 # AMD Strix Point (Radeon 890M / 880M)
|
||||
brew install rocmfpx --with-gfx120X # AMD RDNA4 Discrete GPUs (RX 9070 XT / 9070)
|
||||
brew install rocmfpx --with-gfx110X # AMD RDNA3 GPUs (RX 7900 XTX / 7800 XT / Radeon 780M)
|
||||
brew install rocmfpx --with-gfx103X # AMD RDNA2 / Steam Deck (Van Gogh / 680M / RX 6800)
|
||||
brew install rocmfpx --with-gfx90a # AMD Instinct MI210 / MI250X (CDNA2)
|
||||
brew install rocmfpx --with-gfx908 # AMD Instinct MI100 (CDNA1)
|
||||
# Build / Install with NVIDIA CUDA acceleration:
|
||||
brew install <formula> --with-cuda
|
||||
|
||||
# Start OpenAI-compatible server:
|
||||
rocmfpx -m /path/to/model.gguf --port 8080
|
||||
# Build / Install with Apple Metal acceleration (macOS):
|
||||
brew install <formula> --with-metal
|
||||
|
||||
# Build / Install with Vulkan SPIR-V acceleration:
|
||||
brew install <formula> --with-vulkan
|
||||
|
||||
# Fallback to portable CPU-only builds:
|
||||
brew install <formula> --with-cpu
|
||||
```
|
||||
|
||||
### Passing Custom ROCm Paths
|
||||
Because Homebrew sanitizes environmental variables during `brew install`, supply the ROCm installation path via `HOMEBREW_ROCM_PATH`:
|
||||
```bash
|
||||
HOMEBREW_ROCM_PATH=/opt/rocm-nightly brew install gufo --with-rocm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🎨 `stable-diffusion-cpp`
|
||||
Fast, lightweight image and video generation engine based on [`leejet/stable-diffusion.cpp`](https://github.com/leejet/stable-diffusion.cpp) in pure C/C++. Supports Stable Diffusion 1.x/2.x, SDXL, SD3, Flux, and Wan video models:
|
||||
## 🔄 End-to-End Nightly CI/CD Pipeline Architecture
|
||||
|
||||
The tap features a matrixed CI/CD automation pipeline configured in [`.github/workflows/nightly-matrix.yml`](.github/workflows/nightly-matrix.yml) that executes on a nightly schedule (`02:00 UTC`) or manually via `workflow_dispatch`:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Nightly Trigger (02:00 UTC) / Manual Dispatch"] --> B["Change Gate (check_upstreams.py)"]
|
||||
B --> C1["Query Git Remotes (git ls-remote)"]
|
||||
B --> C2["Query AMD TheRock ROCm 10.1.0a+ Index"]
|
||||
B --> C3["Diff Against Ledger (.github/upstream-pins.json)"]
|
||||
|
||||
C1 & C2 & C3 --> D{"Any Dirty Targets?"}
|
||||
D -- "No Changes" --> E["Clean No-Op Exit"]
|
||||
D -- "Changes Detected" --> F["Dynamic GitHub Actions Matrix"]
|
||||
|
||||
F --> G1["Hosted Linux Runners (Ubuntu 24.04)<br/>- CPU Builds<br/>- CUDA 12.8 Containers<br/>- Dynamic ROCm 10.1.0a+ Resolver"]
|
||||
F --> G2["Hosted macOS 14 Runners (Apple Silicon)<br/>- Native Metal Builds"]
|
||||
F --> G3["Self-Hosted Strix Halo Runner<br/>- Physical gfx1151 Acceleration"]
|
||||
|
||||
G1 & G2 & G3 --> H["3-Tier Validation Harness (validate_bottle.py)"]
|
||||
H --> I1["Tier 1: Dynamic Linker & ABI Audit (ldd / otool)"]
|
||||
H --> I2["Tier 2: Device Discovery Smoke Test"]
|
||||
H --> I3["Tier 3: Synthetic Deterministic Inference Run"]
|
||||
|
||||
I1 & I2 & I3 --> J["Cryptographic Proof Log (validation-proof-*.json)"]
|
||||
J --> K["Bottle Packaging & GitHub Releases"]
|
||||
K --> L["Ledger Synchronization & Main Branch Commit"]
|
||||
```
|
||||
|
||||
### 1. Dynamic AMD TheRock ROCm 10.1.0a+ Resolver ([`.github/scripts/resolve_rocm_sdk.sh`](.github/scripts/resolve_rocm_sdk.sh))
|
||||
- Primary index: `https://nightly.repo.amd.com/rocm/core/tarball/` (fallback to `https://rocm.nightlies.amd.com/tarball-multi-arch/`).
|
||||
- Resolves daily `10.1.0a+` nightly archives matching `therock-dist-linux-gfx1151-*.tar.gz`, `gfx1150`, or multiarch tarballs.
|
||||
- Filters out non-runtime packages (such as `-tests-`).
|
||||
- Supports ETag and filename HTTP caching to prevent redundant bandwidth downloads.
|
||||
- Unpacks to `/opt/rocm-nightly`, sets `HIP_PATH`, `ROCM_PATH`, and configures clang++ / lld symlinks.
|
||||
|
||||
### 2. State Ledger & Change Gate ([`.github/upstream-pins.json`](.github/upstream-pins.json) & [`.github/scripts/check_upstreams.py`](.github/scripts/check_upstreams.py))
|
||||
- Tracks current git commit SHAs, release versions, ROCm build timestamps, and formula `.rb` checksums for all 15 projects.
|
||||
- Detects new upstream commits and ROCm nightly updates.
|
||||
- Skips unsupported hardware configurations with explicit reason strings (preventing fake or broken backend compilation).
|
||||
- Emits a clean JSON matrix for GitHub Actions. If everything is up to date, exits cleanly without launching runners.
|
||||
|
||||
### 3. Three-Tier Validation Harness ([`.github/scripts/validate_bottle.py`](.github/scripts/validate_bottle.py))
|
||||
Every built bottle undergoes rigorous verification before publication:
|
||||
1. **Tier 1 (Linkage & ABI Audit)**: Runs `ldd` (Linux) or `otool -L` (macOS) to verify mandatory accelerator libraries (`libamdhip64.so`, `libhipblaslt.so`, `libcuda.so`, `Metal.framework`) link without orphan symbols or `not found` records.
|
||||
2. **Tier 2 (Device Discovery Smoke Test)**: Executes `--list-devices`, `--info`, or backend diagnostics to verify hardware hooks initialize properly.
|
||||
3. **Tier 3 (Deterministic Inference Run)**: Executes a synthetic golden test pass with assertions on exit code and throughput.
|
||||
4. **Proof Generation**: Emits a cryptographically verified `validation-proof-<formula>-<variant>.json` log containing SHA256 checksums, dynamic linker outputs, compiler options, and runtime telemetry.
|
||||
|
||||
### 4. Supply Chain Security & Sigstore Attestation
|
||||
Nightly bottles and proof logs published to GitHub Releases are attested using GitHub Actions Artifact Attestations (Sigstore):
|
||||
```bash
|
||||
# Default: Vulkan RADV acceleration on Linux; Metal on Apple Silicon
|
||||
brew install stable-diffusion-cpp
|
||||
|
||||
# On Linux, optional acceleration flags:
|
||||
brew install stable-diffusion-cpp --with-rocm # AMD ROCm GPU acceleration (ROCm 7+ / 10.1+)
|
||||
brew install stable-diffusion-cpp --with-cpu # CPU-only baseline
|
||||
|
||||
# Run text-to-image generation:
|
||||
sd -m /path/to/sdxl.gguf -p "A cinematic view of an ancient library on Mars" -o output.png
|
||||
|
||||
# Launch HTTP generation server:
|
||||
sd-server --host 0.0.0.0 --port 8080
|
||||
# Verify cryptographic provenance of any downloaded bottle:
|
||||
gh attestation verify bottle.tar.gz --owner Heretek-AI
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🦀 `shimmy`
|
||||
A pure-Rust LLM engine by Michael Kuykendall ([`Michael-A-Kuykendall/shimmy`](https://github.com/Michael-A-Kuykendall/shimmy)) using `wgpu` and native CUDA/WebGPU shaders. Single binary with sub-second cold starts and zero Python or C/C++ dependencies.
|
||||
## 💻 Local Tap Testing & Development
|
||||
|
||||
Run the complete test suite locally:
|
||||
|
||||
```bash
|
||||
brew install shimmy
|
||||
# Run Python unit tests for resolvers, validators, and gates:
|
||||
pytest tests/ -v
|
||||
|
||||
# Run OpenAI-compatible inference server on port 11434 (drop-in Ollama replacement):
|
||||
shimmy serve --port 11434 --host 0.0.0.0
|
||||
```
|
||||
# Run dry-run upstream change check:
|
||||
python3 .github/scripts/check_upstreams.py --dry-run
|
||||
|
||||
---
|
||||
# Test formula syntax and auditing inside Docker:
|
||||
docker run --rm -v "${PWD}:/tap:ro" ghcr.io/homebrew/ubuntu24.04 bash -c \
|
||||
"brew style /tap/Formula/*.rb"
|
||||
|
||||
### 🦉 `gufo` / `gufo-franzmoca` / `gufetto` (AMD Strix Halo gfx1151 Native Engines)
|
||||
|
||||
Pure C11/C++20/C++23/HIP inference engines built specifically for the AMD Ryzen AI Max+ 395 ("Strix Halo", `gfx1151`) APU:
|
||||
|
||||
- **`gufo`** ([`gufo-org/gufo`](https://github.com/gufo-org/gufo)): Modular Source Extension Architecture, `gufo speak` (voice assistant), `gufo decide`, `/v1/realtime` WebSockets, and Qwen-Image-2.1-Turbo.
|
||||
- **`gufo-franzmoca`** ([`franzmoca/gufo`](https://github.com/franzmoca/gufo)): 763B DeepSeek-V4.1-Flash (at up to 1M context with NVMe `--stream-experts` and dual-SSD split streaming) + Gemma 4 (26B-A4B & 31B) with MTP speculative decoding.
|
||||
- **`gufetto`** ([`fewtarius/gufetto`](https://github.com/fewtarius/gufetto)): C++23 runtime with Flash-Next rotated Q4/Q8 KV cache, multi-prefix pinning (`--pin-prefix-dir`), and Measured Model Admission Planner (`gufo diagnose --plan`).
|
||||
|
||||
```bash
|
||||
# Install canonical upstream gufo:
|
||||
brew install gufo
|
||||
|
||||
# Or install franzmoca's DeepSeek-V4.1-Flash & Gemma 4 engine:
|
||||
brew install gufo-franzmoca
|
||||
|
||||
# Or install fewtarius's rotated KV cache & multi-prefix pinning runtime:
|
||||
brew install gufetto
|
||||
|
||||
# Start OpenAI-compatible server on port 8080:
|
||||
gufo serve --host 127.0.0.1 --port 8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ⚡ `strixite` (Zero-Dependency Qwen3.8-Flash-Next Strix Halo Engine)
|
||||
From-scratch C++20/HIP inference runtime by Shawn Shekari ([`shawnshekari/strixite`](https://github.com/shawnshekari/strixite)) for Strix Halo (`gfx1151`). Converts safetensors directly to `.strixw` v2 weights with zero external library dependencies.
|
||||
|
||||
```bash
|
||||
brew install strixite
|
||||
|
||||
# Convert HuggingFace safetensors:
|
||||
convert_qwen4exp /path/to/hf-weights /path/to/strixw --int4-shared
|
||||
|
||||
# Launch server on port 5300:
|
||||
strix_server --model /path/to/strixw --port 5300 --kv-mode fp8 --mtp-draft 3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🏔️ `strata` & `project-maya` (Tiered MoE Multi-Model Engines)
|
||||
Hybrid C++/Python engines designed for tiered GPU/CPU MoE inference across Strix Halo, discrete AMD RDNA3/RDNA4 GPUs, Apple Silicon Metal, and NVIDIA CUDA:
|
||||
|
||||
- **`strata`** ([`Niko1221/Strata`](https://github.com/Niko1221/Strata)): Tiered inference for Qwen3.8-Flash-Next, Qwen3.6-35B-A3B, and DeepSeek-V4.1-Flash with multi-GPU splitting (`strata-split-2/3/4`).
|
||||
- **`project-maya`** ([`mw00/project-maya`](https://github.com/mw00/project-maya)): Tiered inference for GLM-5.3-Flash, Qwen-3.8, DeepSeek-V4.1-Flash, and MiniMax-M3.1-Flash with runtime CPUID AVX-512 expert kernels.
|
||||
|
||||
```bash
|
||||
# Install Strata:
|
||||
brew install strata
|
||||
|
||||
# Install Project Maya:
|
||||
brew install project-maya
|
||||
|
||||
# Launch servers:
|
||||
strata-cli --model qwen38 --port 8095
|
||||
maya-cli --model glm51 --port 8095
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🥋 `kyojin` (ExLlamaV3 Strix Halo & RDNA4 MoE Suite)
|
||||
Python + C++20/HIP fork of ExLlamaV3 by Yamz Labs ([`Yamz-Labs/kyojin`](https://github.com/Yamz-Labs/kyojin)) featuring 5 specialized model servers (`qwen`, `glm`, `mimo`, `ds41`, `flint`) with automatic Strix Halo ROCm runtime environment configuration.
|
||||
|
||||
```bash
|
||||
brew install kyojin
|
||||
|
||||
# Start dedicated server:
|
||||
kyojin-serve qwen -m /path/to/qwen-exl3 --mtp --port 8000
|
||||
kyojin-serve ds41 -m /path/to/dsv41-gguf --ngram --port 8000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🚀 `hyperqwen` (NVIDIA CUDA 13.0 Qwen3.8-27B Appliance)
|
||||
Turnkey NVIDIA CUDA 13.0 (`sm_80`/`sm_86`/`sm_89`/`sm_120`) appliance by Syv AI ([`syv-ai/HyperQwen`](https://github.com/syv-ai/HyperQwen)) wrapping a patched vLLM 0.32.0 distribution with custom inline PTX Flash Attention and Local Symbol Acceleration (`liblsa.so`).
|
||||
|
||||
```bash
|
||||
brew install hyperqwen
|
||||
|
||||
# Start OpenAI-compatible vLLM server on port 18020:
|
||||
hyperqwen-serve
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ In-Repo Automated Building & Maintenance
|
||||
|
||||
All builder workflows are consolidated directly into this repository:
|
||||
|
||||
- **`.github/workflows/build-bottles.yml`**: Nightly and dispatch builds of Linux x86_64 bottles across all 11 formulas with AMD TheRock ROCm runtime packaging (dynamic auto-resolution of latest ROCm 7+ / 10.1+ SDK).
|
||||
- **`.github/workflows/build-rocmfpx.yml`**: Nightly and dispatch builds of `charlie12345/ROCmFPX` across Ubuntu & Windows with ROCm runtime packaging (dynamic auto-resolution of latest ROCm 7+ / 10.1+ SDK).
|
||||
- **`.github/workflows/autobump.yml`**: Scheduled synchronization for upstream releases, commit SHAs, and formula checksums.
|
||||
- **`.github/workflows/test-install-and-run.yml`**: Multi-OS install and binary validation suite.
|
||||
|
||||
```bash
|
||||
# Test formula updater locally:
|
||||
python3 scripts/update_formulae.py --repo all --dry-run
|
||||
# Run ShellCheck on all bash scripts:
|
||||
docker run --rm -v "${PWD}:/mnt" -w /mnt koalaman/shellcheck:stable \
|
||||
.github/scripts/resolve_rocm_sdk.sh .github/scripts/setup_ccache.sh scripts/test_local_tap.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
This Homebrew Tap and its automation scripts are licensed under the [MIT License](LICENSE). Individual upstream packages remain licensed under their respective open-source licenses.
|
||||
This Homebrew Tap and its CI/CD automation infrastructure are licensed under the [MIT License](LICENSE). Individual upstream packages remain licensed under their respective open-source licenses.
|
||||
+123
-77
@@ -18,79 +18,110 @@ import urllib.request
|
||||
|
||||
GITHUB_API_BASE = "https://api.github.com/repos"
|
||||
|
||||
CONFIG = {
|
||||
# In-repo multi-arch ROCm release tracking
|
||||
"rocmfpx": {
|
||||
"type": "builder",
|
||||
"repo": "Heretek-AI/homebrew-tap",
|
||||
"fallback_repo": "Heretek-AI/ROCmFPX-BUILDER",
|
||||
"formulae": ["rocmfpx.rb"],
|
||||
},
|
||||
def load_config_from_ledger():
|
||||
ledger_path = os.path.join(os.path.dirname(__file__), "..", ".github", "upstream-pins.json")
|
||||
if os.path.exists(ledger_path):
|
||||
try:
|
||||
with open(ledger_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
packages = data.get("packages", {})
|
||||
cfg = {}
|
||||
for name, pdata in packages.items():
|
||||
track = pdata.get("track", "commit")
|
||||
if track == "commit":
|
||||
ttype = "upstream_commit"
|
||||
elif track == "release":
|
||||
ttype = "upstream_release"
|
||||
elif track == "rolling_release":
|
||||
ttype = "rolling_release"
|
||||
elif track == "builder":
|
||||
ttype = "builder"
|
||||
else:
|
||||
ttype = "upstream_commit"
|
||||
|
||||
# Upstream source release tracking
|
||||
"shimmy": {
|
||||
"type": "upstream_release",
|
||||
"repo": "Michael-A-Kuykendall/shimmy",
|
||||
"formulae": ["shimmy.rb"],
|
||||
},
|
||||
entry = {
|
||||
"type": ttype,
|
||||
"repo": pdata.get("repo", name),
|
||||
"branch": pdata.get("branch", "main"),
|
||||
"formulae": [f"{pdata.get('formula', name)}.rb"]
|
||||
}
|
||||
if name == "rocmfpx":
|
||||
entry["fallback_repo"] = "Heretek-AI/ROCmFPX-BUILDER"
|
||||
cfg[name] = entry
|
||||
if cfg:
|
||||
return cfg
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Upstream rolling release tracking (stable-diffusion.cpp)
|
||||
"stable-diffusion-cpp": {
|
||||
"type": "rolling_release",
|
||||
"repo": "leejet/stable-diffusion.cpp",
|
||||
"formulae": ["stable-diffusion-cpp.rb"],
|
||||
},
|
||||
return {
|
||||
# Fallback in-repo multi-arch ROCm release tracking
|
||||
"rocmfpx": {
|
||||
"type": "builder",
|
||||
"repo": "Heretek-AI/homebrew-tap",
|
||||
"fallback_repo": "Heretek-AI/ROCmFPX-BUILDER",
|
||||
"formulae": ["rocmfpx.rb"],
|
||||
},
|
||||
"shimmy": {
|
||||
"type": "upstream_release",
|
||||
"repo": "Michael-A-Kuykendall/shimmy",
|
||||
"formulae": ["shimmy.rb"],
|
||||
},
|
||||
"stable-diffusion-cpp": {
|
||||
"type": "rolling_release",
|
||||
"repo": "leejet/stable-diffusion.cpp",
|
||||
"formulae": ["stable-diffusion-cpp.rb"],
|
||||
},
|
||||
"gufetto": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "community-token-labs/gufetto",
|
||||
"branch": "main",
|
||||
"formulae": ["gufetto.rb"],
|
||||
},
|
||||
"gufo": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "gufo-org/gufo",
|
||||
"branch": "main",
|
||||
"formulae": ["gufo.rb"],
|
||||
},
|
||||
"gufo-franzmoca": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "franzmoca/gufo",
|
||||
"branch": "main",
|
||||
"formulae": ["gufo-franzmoca.rb"],
|
||||
},
|
||||
"strixite": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "shawnshekari/strixite",
|
||||
"branch": "main",
|
||||
"formulae": ["strixite.rb"],
|
||||
},
|
||||
"strata": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "Niko1221/Strata",
|
||||
"branch": "main",
|
||||
"formulae": ["strata.rb"],
|
||||
},
|
||||
"project-maya": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "mw00/project-maya",
|
||||
"branch": "main",
|
||||
"formulae": ["project-maya.rb"],
|
||||
},
|
||||
"kyojin": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "Yamz-Labs/kyojin",
|
||||
"branch": "main",
|
||||
"formulae": ["kyojin.rb"],
|
||||
},
|
||||
"hyperqwen": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "syv-ai/HyperQwen",
|
||||
"branch": "main",
|
||||
"formulae": ["hyperqwen.rb"],
|
||||
},
|
||||
}
|
||||
|
||||
# Local LLM engines & inference runtimes
|
||||
"gufetto": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "fewtarius/gufetto",
|
||||
"branch": "main",
|
||||
"formulae": ["gufetto.rb"],
|
||||
},
|
||||
"gufo": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "gufo-org/gufo",
|
||||
"branch": "main",
|
||||
"formulae": ["gufo.rb"],
|
||||
},
|
||||
"gufo-franzmoca": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "franzmoca/gufo",
|
||||
"branch": "main",
|
||||
"formulae": ["gufo-franzmoca.rb"],
|
||||
},
|
||||
"strixite": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "shawnshekari/strixite",
|
||||
"branch": "main",
|
||||
"formulae": ["strixite.rb"],
|
||||
},
|
||||
"strata": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "Niko1221/Strata",
|
||||
"branch": "main",
|
||||
"formulae": ["strata.rb"],
|
||||
},
|
||||
"project-maya": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "mw00/project-maya",
|
||||
"branch": "main",
|
||||
"formulae": ["project-maya.rb"],
|
||||
},
|
||||
"kyojin": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "Yamz-Labs/kyojin",
|
||||
"branch": "main",
|
||||
"formulae": ["kyojin.rb"],
|
||||
},
|
||||
"hyperqwen": {
|
||||
"type": "upstream_commit",
|
||||
"repo": "syv-ai/HyperQwen",
|
||||
"branch": "main",
|
||||
"formulae": ["hyperqwen.rb"],
|
||||
},
|
||||
}
|
||||
CONFIG = load_config_from_ledger()
|
||||
|
||||
|
||||
def fetch_json(url: str, token: str = None) -> dict:
|
||||
@@ -254,21 +285,26 @@ def update_upstream_release_formula(formula_path: str, repo: str, token: str = N
|
||||
return False
|
||||
|
||||
|
||||
def update_upstream_commit_formula(formula_path: str, repo: str, branch: str = "main", token: str = None, dry_run: bool = False) -> bool:
|
||||
def update_upstream_commit_formula(formula_path: str, repo: str, branch: str = "main", token: str = None, commit_sha: str = None, sha256_digest: str = None, dry_run: bool = False) -> bool:
|
||||
if not os.path.exists(formula_path):
|
||||
print(f"[-] Formula not found: {formula_path}")
|
||||
return False
|
||||
|
||||
commit_data = get_latest_commit(repo, branch, token)
|
||||
commit_sha = commit_data.get("sha", "")
|
||||
commit_date = commit_data.get("commit", {}).get("committer", {}).get("date", "")[:10].replace("-", ".")
|
||||
if not commit_sha:
|
||||
commit_data = get_latest_commit(repo, branch, token)
|
||||
commit_sha = commit_data.get("sha", "")
|
||||
commit_date = commit_data.get("commit", {}).get("committer", {}).get("date", "")[:10].replace("-", ".")
|
||||
else:
|
||||
commit_date = time.strftime("%Y.%m.%d", time.gmtime())
|
||||
|
||||
if not commit_sha:
|
||||
print(f"[-] No commit SHA found for {repo}/{branch}")
|
||||
return False
|
||||
|
||||
tarball_url = f"https://github.com/{repo}/archive/{commit_sha}.tar.gz"
|
||||
print(f"[*] Computing SHA256 for commit {commit_sha[:7]} ({tarball_url})...")
|
||||
sha256_digest = download_and_compute_sha256(tarball_url, token)
|
||||
if not sha256_digest:
|
||||
print(f"[*] Computing SHA256 for commit {commit_sha[:7]} ({tarball_url})...")
|
||||
sha256_digest = download_and_compute_sha256(tarball_url, token)
|
||||
|
||||
with open(formula_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
@@ -420,6 +456,8 @@ def main():
|
||||
help="Target builder repo or formula key",
|
||||
)
|
||||
parser.add_argument("--tag", default="latest", help="Specific release tag or 'latest'")
|
||||
parser.add_argument("--commit", default=None, help="Explicit target commit SHA for upstream_commit formulae")
|
||||
parser.add_argument("--sha256", default=None, help="Precomputed sha256 checksum for the archive")
|
||||
parser.add_argument("--token", default=os.environ.get("GITHUB_TOKEN"), help="GitHub API token")
|
||||
parser.add_argument("--formula-dir", default=os.path.join(os.path.dirname(__file__), "..", "Formula"), help="Path to Formula directory")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print updates without modifying files")
|
||||
@@ -506,7 +544,15 @@ def main():
|
||||
elif target_type == "upstream_commit":
|
||||
branch = cfg.get("branch", "main")
|
||||
try:
|
||||
changed = update_upstream_commit_formula(formula_path, repo_name, branch, args.token, args.dry_run)
|
||||
changed = update_upstream_commit_formula(
|
||||
formula_path,
|
||||
repo_name,
|
||||
branch=branch,
|
||||
token=args.token,
|
||||
commit_sha=args.commit,
|
||||
sha256_digest=args.sha256,
|
||||
dry_run=args.dry_run
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"[-] Failed to update upstream commit formula {formula_file}: {e}")
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>ROCm SDK nightly tarballs</title></head>
|
||||
<body>
|
||||
<script>
|
||||
const files = [
|
||||
{"name": "therock-dist-linux-gfx1151-7.15.0a20260728.tar.gz", "mtime": 1785000000.0},
|
||||
{"name": "therock-dist-linux-gfx1151-10.1.0a20260811+bkc.20260818.tar.gz", "mtime": 1786000000.0},
|
||||
{"name": "therock-dist-linux-gfx1151-10.1.0a20260822.tar.gz", "mtime": 1787000000.0},
|
||||
{"name": "therock-dist-linux-multiarch-10.1.0a20260822.tar.gz", "mtime": 1787000000.0},
|
||||
{"name": "therock-dist-linux-multiarch-tests-10.1.0a20260822.tar.gz", "mtime": 1787000000.0}
|
||||
];
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Tarball index</title></head>
|
||||
<body>
|
||||
<script>
|
||||
const files = [
|
||||
{"name": "therock-dist-linux-gfx1151-10.1.0a20260901.tar.gz", "mtime": 1788227235.0},
|
||||
{"name": "therock-dist-linux-gfx1151-10.1.0a20260910.tar.gz", "mtime": 1789000000.0},
|
||||
{"name": "therock-dist-linux-gfx1151-tests-10.3.0a20261105.tar.gz", "mtime": 1793000000.0},
|
||||
{"name": "therock-dist-linux-gfx1151-10.2.0a20261001.tar.gz", "mtime": 1791000000.0},
|
||||
{"name": "therock-dist-linux-gfx1151-10.3.0a20261105.tar.gz", "mtime": 1794000000.0},
|
||||
{"name": "therock-dist-linux-multiarch-10.3.0a20261105.tar.gz", "mtime": 1794000000.0},
|
||||
{"name": "therock-dist-linux-multiarch-tests-10.3.0a20261105.tar.gz", "mtime": 1794000000.0},
|
||||
{"name": "therock-dist-linux-gfx1150-10.3.0a20261105.tar.gz", "mtime": 1794000000.0},
|
||||
{"name": "therock-dist-linux-gfx110X-all-10.3.0a20261105.tar.gz", "mtime": 1794000000.0},
|
||||
{"name": "therock-dist-linux-gfx90a-10.3.0a20261105.tar.gz", "mtime": 1794000000.0},
|
||||
{"name": "therock-dist-linux-gfx94X-dcgpu-10.3.0a20261105.tar.gz", "mtime": 1794000000.0}
|
||||
];
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,116 @@
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
SCRIPT_PATH = Path(__file__).resolve().parent.parent / ".github" / "scripts" / "check_upstreams.py"
|
||||
FIXTURES_DIR = Path(__file__).resolve().parent / "fixtures"
|
||||
|
||||
|
||||
def test_check_upstreams_dry_run():
|
||||
cmd = [sys.executable, str(SCRIPT_PATH), "--dry-run"]
|
||||
res = subprocess.run(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
assert res.returncode == 0, f"check_upstreams failed: {res.stderr}"
|
||||
assert "Active Jobs Scheduled" in res.stdout or "Total jobs to execute:" in res.stdout
|
||||
assert "Excluded" in res.stdout or "SKIPPED" in res.stdout
|
||||
|
||||
|
||||
def test_check_upstreams_custom_ledger(tmp_path):
|
||||
test_ledger = {
|
||||
"schema_version": 1,
|
||||
"rocm": {
|
||||
"min_version": "10.1.0",
|
||||
"last_build_stamp": "20261105"
|
||||
},
|
||||
"packages": {
|
||||
"testpkg": {
|
||||
"formula": "testpkg",
|
||||
"remote": "https://github.com/example/testpkg.git",
|
||||
"repo": "example/testpkg",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"variants": {
|
||||
"cpu": {
|
||||
"status": "enabled",
|
||||
"backend": "cpu",
|
||||
"default": True,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"last_built_commit": "abc1234",
|
||||
"last_rocm_stamp": None
|
||||
},
|
||||
"metal": {
|
||||
"status": "excluded",
|
||||
"reason": "Not supported"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ledger_file = tmp_path / "ledger.json"
|
||||
ledger_file.write_text(json.dumps(test_ledger), encoding="utf-8")
|
||||
|
||||
# Run with offline fixtures to avoid network calls
|
||||
fixtures_dir = tmp_path / "fixtures"
|
||||
fixtures_dir.mkdir()
|
||||
(fixtures_dir / "testpkg.json").write_text(json.dumps({"target": "abc1234"}), encoding="utf-8")
|
||||
(fixtures_dir / "rocm_gfx1151.json").write_text(json.dumps({"build_stamp": "20261105", "filename": "dummy.tar.gz"}), encoding="utf-8")
|
||||
|
||||
cmd = [
|
||||
sys.executable, str(SCRIPT_PATH),
|
||||
"--ledger", str(ledger_file),
|
||||
"--offline-fixtures", str(fixtures_dir),
|
||||
"--dry-run"
|
||||
]
|
||||
res = subprocess.run(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
assert res.returncode == 0
|
||||
assert "Total jobs to execute: 0" in res.stdout
|
||||
|
||||
|
||||
def test_check_upstreams_detects_update(tmp_path):
|
||||
test_ledger = {
|
||||
"schema_version": 1,
|
||||
"rocm": {
|
||||
"min_version": "10.1.0",
|
||||
"last_build_stamp": "20261105"
|
||||
},
|
||||
"packages": {
|
||||
"testpkg": {
|
||||
"formula": "testpkg",
|
||||
"remote": "https://github.com/example/testpkg.git",
|
||||
"repo": "example/testpkg",
|
||||
"branch": "main",
|
||||
"track": "commit",
|
||||
"variants": {
|
||||
"cpu": {
|
||||
"status": "enabled",
|
||||
"backend": "cpu",
|
||||
"default": True,
|
||||
"runner": "ubuntu-24.04",
|
||||
"bottle_tag": "x86_64_linux",
|
||||
"last_built_commit": "oldcommit123",
|
||||
"last_rocm_stamp": None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ledger_file = tmp_path / "ledger.json"
|
||||
ledger_file.write_text(json.dumps(test_ledger), encoding="utf-8")
|
||||
|
||||
fixtures_dir = tmp_path / "fixtures"
|
||||
fixtures_dir.mkdir()
|
||||
(fixtures_dir / "testpkg.json").write_text(json.dumps({"target": "newcommit456"}), encoding="utf-8")
|
||||
(fixtures_dir / "rocm_gfx1151.json").write_text(json.dumps({"build_stamp": "20261105", "filename": "dummy.tar.gz"}), encoding="utf-8")
|
||||
|
||||
cmd = [
|
||||
sys.executable, str(SCRIPT_PATH),
|
||||
"--ledger", str(ledger_file),
|
||||
"--offline-fixtures", str(fixtures_dir),
|
||||
"--dry-run"
|
||||
]
|
||||
res = subprocess.run(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
assert res.returncode == 0
|
||||
assert "Total jobs to execute: 1" in res.stdout
|
||||
assert "upstream-update" in res.stdout
|
||||
@@ -0,0 +1,61 @@
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_PATH = Path(__file__).resolve().parent.parent / ".github" / "scripts" / "finalize_run.py"
|
||||
|
||||
|
||||
def test_finalize_run_synchronization(tmp_path):
|
||||
test_ledger = {
|
||||
"schema_version": 1,
|
||||
"rocm": {
|
||||
"last_build_stamp": None,
|
||||
"last_index_url": None
|
||||
},
|
||||
"packages": {
|
||||
"gufo": {
|
||||
"formula": "gufo",
|
||||
"variants": {
|
||||
"rocm": {
|
||||
"backend": "rocm",
|
||||
"last_built_commit": "old_commit",
|
||||
"last_rocm_stamp": None,
|
||||
"last_success_at": None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ledger_file = tmp_path / "upstream-pins.json"
|
||||
ledger_file.write_text(json.dumps(test_ledger), encoding="utf-8")
|
||||
|
||||
proofs_dir = tmp_path / "proofs"
|
||||
proofs_dir.mkdir()
|
||||
proof_data = {
|
||||
"formula": "gufo",
|
||||
"variant": "rocm",
|
||||
"overall_status": "PASSED",
|
||||
"target_commit": "new_commit_789",
|
||||
"proof_sha256": "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
|
||||
}
|
||||
(proofs_dir / "validation-proof-gufo-rocm.json").write_text(json.dumps(proof_data), encoding="utf-8")
|
||||
|
||||
cmd = [
|
||||
sys.executable, str(SCRIPT_PATH),
|
||||
"--ledger", str(ledger_file),
|
||||
"--proofs-dir", str(proofs_dir),
|
||||
"--rocm-stamp", "20261105",
|
||||
"--rocm-index", "https://nightly.repo.amd.com/rocm/core/tarball/"
|
||||
]
|
||||
res = subprocess.run(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
assert res.returncode == 0, f"finalize_run failed: {res.stderr}"
|
||||
assert "1 Passed" in res.stdout
|
||||
|
||||
updated_ledger = json.loads(ledger_file.read_text(encoding="utf-8"))
|
||||
assert updated_ledger["rocm"]["last_build_stamp"] == "20261105"
|
||||
assert updated_ledger["rocm"]["last_index_url"] == "https://nightly.repo.amd.com/rocm/core/tarball/"
|
||||
gufo_var = updated_ledger["packages"]["gufo"]["variants"]["rocm"]
|
||||
assert gufo_var["last_built_commit"] == "new_commit_789"
|
||||
assert gufo_var["last_rocm_stamp"] == "20261105"
|
||||
assert gufo_var["last_success_at"] is not None
|
||||
@@ -0,0 +1,60 @@
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
SCRIPT_PATH = Path(__file__).resolve().parent.parent / ".github" / "scripts" / "resolve_rocm_sdk.sh"
|
||||
FIXTURES_DIR = Path(__file__).resolve().parent / "fixtures"
|
||||
|
||||
has_bash = bool(shutil.which("bash")) or bool(shutil.which("wsl"))
|
||||
|
||||
|
||||
def to_wsl_path(p):
|
||||
s = str(p).replace("\\", "/")
|
||||
if len(s) > 1 and s[1] == ":":
|
||||
return f"/mnt/{s[0].lower()}/{s[2:].lstrip('/')}"
|
||||
return s
|
||||
|
||||
|
||||
def run_resolve_script(args):
|
||||
cmd = ["bash", str(SCRIPT_PATH)] + args
|
||||
res = subprocess.run(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
if res.returncode != 0 and shutil.which("wsl"):
|
||||
wsl_script = to_wsl_path(SCRIPT_PATH)
|
||||
wsl_args = [to_wsl_path(a) if (isinstance(a, Path) or ("\\" in str(a) or ":/" in str(a))) else str(a) for a in args]
|
||||
res = subprocess.run(["wsl", "bash", wsl_script] + wsl_args, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
return res
|
||||
|
||||
|
||||
@pytest.mark.skipif(not has_bash, reason="bash or wsl is required to execute resolve_rocm_sdk.sh")
|
||||
def test_resolve_primary_index_latest():
|
||||
fixture = FIXTURES_DIR / "rocm_index_primary.html"
|
||||
res = run_resolve_script(["--index-file", str(fixture), "--family", "gfx1151", "--resolve-only", "--json"])
|
||||
assert res.returncode == 0, f"Script failed: {res.stderr}"
|
||||
data = json.loads(res.stdout)
|
||||
assert data["filename"] == "therock-dist-linux-gfx1151-10.3.0a20261105.tar.gz"
|
||||
assert data["build_stamp"] == "20261105"
|
||||
assert data["version"] == "10.3.0a"
|
||||
assert "-tests-" not in data["filename"]
|
||||
|
||||
|
||||
@pytest.mark.skipif(not has_bash, reason="bash or wsl is required to execute resolve_rocm_sdk.sh")
|
||||
def test_resolve_max_version_cap():
|
||||
fixture = FIXTURES_DIR / "rocm_index_primary.html"
|
||||
res = run_resolve_script(["--index-file", str(fixture), "--family", "gfx1151", "--max-version", "10.1.99", "--resolve-only", "--json"])
|
||||
assert res.returncode == 0, f"Script failed: {res.stderr}"
|
||||
data = json.loads(res.stdout)
|
||||
assert data["filename"] == "therock-dist-linux-gfx1151-10.1.0a20260910.tar.gz"
|
||||
assert data["build_stamp"] == "20260910"
|
||||
|
||||
|
||||
@pytest.mark.skipif(not has_bash, reason="bash or wsl is required to execute resolve_rocm_sdk.sh")
|
||||
def test_resolve_legacy_bkc_channel():
|
||||
fixture = FIXTURES_DIR / "rocm_index_legacy.html"
|
||||
res = run_resolve_script(["--index-file", str(fixture), "--family", "gfx1151", "--channel", "bkc", "--resolve-only", "--json"])
|
||||
assert res.returncode == 0, f"Script failed: {res.stderr}"
|
||||
data = json.loads(res.stdout)
|
||||
assert "bkc" in data["filename"]
|
||||
assert data["filename"] == "therock-dist-linux-gfx1151-10.1.0a20260811+bkc.20260818.tar.gz"
|
||||
@@ -0,0 +1,68 @@
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_PATH = Path(__file__).resolve().parent.parent / ".github" / "scripts" / "validate_bottle.py"
|
||||
SPECS_PATH = Path(__file__).resolve().parent.parent / ".github" / "validation-specs.json"
|
||||
|
||||
|
||||
def test_validate_bottle_success(tmp_path):
|
||||
# Create mock keg
|
||||
keg = tmp_path / "keg"
|
||||
bin_dir = keg / "bin"
|
||||
bin_dir.mkdir(parents=True)
|
||||
|
||||
dummy_bin = bin_dir / "gufo"
|
||||
dummy_bin.write_text("#!/usr/bin/env bash\necho 'gufo version 0.28.1'\necho 'No device found (hipErrorNoDevice)'\n", encoding="utf-8")
|
||||
dummy_bin.chmod(0o755)
|
||||
|
||||
out_file = tmp_path / "proof.json"
|
||||
cmd = [
|
||||
sys.executable, str(SCRIPT_PATH),
|
||||
"--keg", str(keg),
|
||||
"--formula", "gufo",
|
||||
"--variant", "rocm",
|
||||
"--spec", str(SPECS_PATH),
|
||||
"--tiers", "1,2",
|
||||
"--hardware", "absent",
|
||||
"--out", str(out_file)
|
||||
]
|
||||
res = subprocess.run(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
assert res.returncode == 0, f"validate_bottle failed: {res.stderr}"
|
||||
|
||||
assert out_file.is_file()
|
||||
data = json.loads(out_file.read_text(encoding="utf-8"))
|
||||
assert data["formula"] == "gufo"
|
||||
assert data["variant"] == "rocm"
|
||||
assert "proof_sha256" in data
|
||||
assert len(data["proof_sha256"]) == 64
|
||||
assert data["overall_status"] in ("PASSED", "PASSED_HW_ABSENT")
|
||||
|
||||
|
||||
def test_validate_bottle_crash_fails(tmp_path):
|
||||
keg = tmp_path / "keg"
|
||||
bin_dir = keg / "bin"
|
||||
bin_dir.mkdir(parents=True)
|
||||
|
||||
# Crashing binary with exit code 139 (SIGSEGV)
|
||||
crash_bin = bin_dir / "gufo"
|
||||
crash_bin.write_text("#!/usr/bin/env bash\nexit 139\n", encoding="utf-8")
|
||||
crash_bin.chmod(0o755)
|
||||
|
||||
out_file = tmp_path / "proof.json"
|
||||
cmd = [
|
||||
sys.executable, str(SCRIPT_PATH),
|
||||
"--keg", str(keg),
|
||||
"--formula", "gufo",
|
||||
"--variant", "rocm",
|
||||
"--spec", str(SPECS_PATH),
|
||||
"--tiers", "2",
|
||||
"--hardware", "absent",
|
||||
"--out", str(out_file)
|
||||
]
|
||||
res = subprocess.run(cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
assert res.returncode != 0
|
||||
data = json.loads(out_file.read_text(encoding="utf-8"))
|
||||
assert data["overall_status"] == "FAILED"
|
||||
Reference in new issue
Block a user