Files
elizaos--eliza/scripts/check-riscv64-artifacts.sh
wehub-resource-sync 426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
ci / test (push) Waiting to run
ci / lint-and-format (push) Waiting to run
ci / build (push) Waiting to run
ci / dev-startup (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
gitleaks / gitleaks (push) Waiting to run
Markdown Links / Relative Markdown Links (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Quality (Extended) / Homepage Build (PR smoke) (push) Waiting to run
Quality (Extended) / Comment-only diff guard (push) Waiting to run
Quality (Extended) / Format + Type Safety Ratchet (push) Waiting to run
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Waiting to run
Quality (Extended) / Develop Gate (lint) (push) Waiting to run
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

449 lines
20 KiB
Bash
Executable File

#!/usr/bin/env bash
# check-riscv64-artifacts.sh — QEMU-user-mode smoke for every riscv64
# native artifact this repo cross-compiles.
#
# One-shot harness: walks the known cross-build outputs (native plugins
# + libllama / libggml family + libsigsys-handler-riscv64),
# confirms each is an ELF UCB RISC-V
# 64-bit double-float-ABI object, and exercises every executable smoke
# under qemu-riscv64-static. Shared libraries are dlopen-verified via a
# tiny C harness (also run under QEMU).
#
# Default posture: this is *gated* on ELIZA_RISCV64_SMOKE=1 because
# the toolchain dependencies (qemu-user-static, the actual riscv64
# artifacts) are heavy and may not be available on every CI box. With
# the env-var unset the script no-ops and exits 0 with a clean
# "skip" marker, so wiring it into a default-CI step is safe.
#
# Usage:
# bash scripts/check-riscv64-artifacts.sh # honor ELIZA_RISCV64_SMOKE
# ELIZA_RISCV64_SMOKE=1 bash scripts/check-riscv64-artifacts.sh
# ELIZA_RISCV64_SMOKE=1 bash scripts/check-riscv64-artifacts.sh --out build/reports/foo.json
# ELIZA_RISCV64_SMOKE=1 bash scripts/check-riscv64-artifacts.sh --no-qemu # ELF-tag check only
#
# Exit code:
# 0 — every artifact is PASS or a documented SKIP
# 1 — at least one artifact FAILed
# 2 — invalid CLI
set -euo pipefail
repo_root="$(cd "$(dirname "$0")/.." && pwd)"
cd "$repo_root"
OUT="$repo_root/build/reports/riscv64_artifacts.json"
QEMU_TIMEOUT="${ELIZA_RISCV64_QEMU_TIMEOUT:-60}"
RUN_QEMU=1
NODE_BIN="${NODE_BIN:-$(command -v node || true)}"
RM_PATH_RECURSIVE="$repo_root/packages/scripts/rm-path-recursive.mjs"
while [ $# -gt 0 ]; do
case "$1" in
--out) OUT="$2"; shift 2;;
--no-qemu) RUN_QEMU=0; shift;;
--timeout) QEMU_TIMEOUT="$2"; shift 2;;
-h|--help)
awk '/^# /{print substr($0,3)} /^#$/{print ""} !/^#/{exit}' "$0"
exit 0;;
*) echo "unknown argument: $1" >&2; exit 2;;
esac
done
mkdir -p "$(dirname "$OUT")"
now_epoch_ms() {
# GNU date supports %N; macOS doesn't, but riscv64 cross-builds are
# Linux-only so this script targets Linux-only callers.
date +%s%3N 2>/dev/null || python3 -c 'import time;print(int(time.time()*1000))'
}
iso_now() { date -u +'%Y-%m-%dT%H:%M:%SZ'; }
remove_path_recursive() {
if [ -z "$NODE_BIN" ]; then
echo "[check-riscv64-artifacts] node not on PATH; cannot remove recursively via $RM_PATH_RECURSIVE." >&2
return 1
fi
"$NODE_BIN" "$RM_PATH_RECURSIVE" "$@"
}
# JSON record buffer. Each record is a single-line JSON object emitted
# as we go, joined into the final array at the end.
TMP_JSON="$(mktemp -t riscv64-artifacts-XXXXXX.jsonl)"
trap 'rm -f "$TMP_JSON"' EXIT
PASS_N=0; FAIL_N=0; SKIP_N=0
emit_record() {
# $1 path, $2 kind (executable|shared|static-archive), $3 status, $4 detail, $5 duration_ms
local path="$1"; local kind="$2"; local status="$3"; local detail="$4"; local dur="$5"
# Properly JSON-escape detail and path.
local esc_path esc_detail
esc_path=$(printf '%s' "$path" | python3 -c 'import sys,json;sys.stdout.write(json.dumps(sys.stdin.read()))')
esc_detail=$(printf '%s' "$detail" | python3 -c 'import sys,json;sys.stdout.write(json.dumps(sys.stdin.read()))')
printf '{"path":%s,"kind":"%s","status":"%s","detail":%s,"duration_ms":%s}\n' \
"$esc_path" "$kind" "$status" "$esc_detail" "$dur" >> "$TMP_JSON"
case "$status" in
PASS) PASS_N=$((PASS_N+1));;
FAIL) FAIL_N=$((FAIL_N+1));;
SKIP) SKIP_N=$((SKIP_N+1));;
esac
printf ' [%-4s] %-70s %s\n' "$status" "${path#$repo_root/}" "$detail"
}
write_final_report() {
local final_status="$1"; local pre_skip_reason="${2:-}"
{
printf '{\n'
printf ' "generated_at": "%s",\n' "$(iso_now)"
printf ' "repo_root": "%s",\n' "$repo_root"
printf ' "eliza_riscv64_smoke": "%s",\n' "${ELIZA_RISCV64_SMOKE:-}"
printf ' "qemu_bin": "%s",\n' "${QEMU_BIN:-}"
printf ' "qemu_run": %s,\n' "$([ "$RUN_QEMU" = "1" ] && echo true || echo false)"
printf ' "qemu_timeout_seconds": %s,\n' "$QEMU_TIMEOUT"
printf ' "summary": {"pass": %d, "fail": %d, "skip": %d},\n' "$PASS_N" "$FAIL_N" "$SKIP_N"
printf ' "final_status": "%s",\n' "$final_status"
if [ -n "$pre_skip_reason" ]; then
local esc
esc=$(printf '%s' "$pre_skip_reason" | python3 -c 'import sys,json;sys.stdout.write(json.dumps(sys.stdin.read()))')
printf ' "pre_skip_reason": %s,\n' "$esc"
fi
printf ' "artifacts": [\n'
if [ -s "$TMP_JSON" ]; then
awk 'NR>1{printf ",\n"} {printf " %s", $0} END{printf "\n"}' "$TMP_JSON"
fi
printf ' ]\n'
printf '}\n'
} > "$OUT"
echo
echo "Report: $OUT"
echo "PASS=$PASS_N SKIP=$SKIP_N FAIL=$FAIL_N"
}
# ── Gate ─────────────────────────────────────────────────────────────
if [ "${ELIZA_RISCV64_SMOKE:-0}" != "1" ]; then
echo "[check-riscv64-artifacts] ELIZA_RISCV64_SMOKE not set; skipping."
echo "[check-riscv64-artifacts] To run: ELIZA_RISCV64_SMOKE=1 bun run check:riscv64-artifacts"
write_final_report "SKIP" "ELIZA_RISCV64_SMOKE!=1 (default-CI gate)"
exit 0
fi
# ── QEMU pre-flight ──────────────────────────────────────────────────
QEMU_BIN="$(command -v qemu-riscv64-static 2>/dev/null || command -v qemu-riscv64 2>/dev/null || true)"
if [ "$RUN_QEMU" = "1" ] && [ -z "$QEMU_BIN" ]; then
cat >&2 <<'EOF'
[check-riscv64-artifacts] qemu-riscv64-static not found.
Install (Debian/Ubuntu):
sudo apt-get install -y qemu-user-static binfmt-support
Install (Fedora/RHEL):
sudo dnf install -y qemu-user-static
Re-run, or pass --no-qemu to ELF-tag-only the artifacts.
Treating this run as SKIP (exit 0) so default CI stays green.
EOF
write_final_report "SKIP" "qemu-riscv64-static missing"
exit 0
fi
echo "[check-riscv64-artifacts] qemu_bin=${QEMU_BIN:-<elf-tag only>} timeout=${QEMU_TIMEOUT}s"
# ── Verifiers ────────────────────────────────────────────────────────
is_riscv64_elf() {
local f="$1"
local info
# `-L` dereferences symlinks (libfoo.so → libfoo.so.1, etc.).
info="$(file -L -b "$f" 2>/dev/null || true)"
case "$info" in
*"UCB RISC-V"*"double-float ABI"*) return 0;;
*) return 1;;
esac
}
ar_members_are_rv64() {
local archive="$1"
case "$archive" in
/*) ;;
*) archive="$(cd "$(dirname "$archive")" && pwd)/$(basename "$archive")";;
esac
local extract_dir="$archive.qemu-extract"
remove_path_recursive "$extract_dir"
mkdir -p "$extract_dir"
if ! ( cd "$extract_dir" && ar x "$archive" >/dev/null 2>&1 ); then
remove_path_recursive "$extract_dir"
return 1
fi
local bad=0 saw=0
for member in "$extract_dir"/*.o; do
[ -f "$member" ] || continue
saw=1
if ! file -b "$member" | grep -q "UCB RISC-V"; then
bad=1; break
fi
done
remove_path_recursive "$extract_dir"
[ "$saw" = "1" ] && [ "$bad" = "0" ]
}
run_executable_under_qemu() {
# $1 path. Echoes "status|detail|duration_ms".
local exe="$1"
if [ ! -x "$exe" ]; then chmod +x "$exe" 2>/dev/null || true; fi
if [ "$RUN_QEMU" != "1" ] || [ -z "$QEMU_BIN" ]; then
echo "SKIP|qemu disabled (elf-tag only)|0"
return
fi
local log; log="$(mktemp -t riscv64-smoke-XXXXXX.log)"
local start end dur ec
start="$(now_epoch_ms)"
if timeout "$QEMU_TIMEOUT" "$QEMU_BIN" "$exe" >"$log" 2>&1; then
end="$(now_epoch_ms)"; dur=$((end - start))
rm -f "$log"
echo "PASS|qemu exit=0|$dur"
else
ec=$?
end="$(now_epoch_ms)"; dur=$((end - start))
local tail; tail="$(tail -c 240 "$log" 2>/dev/null | tr -d '\r' | tr '\n' ' ' | sed 's/ \+/ /g')"
rm -f "$log"
if [ "$ec" = "124" ]; then
echo "FAIL|qemu timeout after ${QEMU_TIMEOUT}s: $tail|$dur"
elif [ "$ec" = "77" ]; then
# Conventional autotools / GNU "skip" exit code — the test
# explicitly punted (usually due to a missing fixture).
echo "SKIP|qemu exit=77 (test self-skipped, likely missing fixture): $tail|$dur"
elif [ "$ec" = "2" ] && printf '%s' "$tail" | grep -qE "[Uu]sage:|--help|missing|not provided|fixture" ; then
# Exit 2 + usage/missing-fixture banner: the test binary
# requires an external argument (model GGUF, parity input,
# etc.) that the smoke harness doesn't supply. Treat as
# SKIP rather than failure — running it correctly is a
# fixture-provisioning task, not a riscv64 correctness gap.
echo "SKIP|qemu exit=2, usage/fixture message: $tail|$dur"
else
echo "FAIL|qemu exit=$ec: $tail|$dur"
fi
fi
}
# Dlopen-verify a .so under QEMU: compiles a tiny C harness for the
# host that just dlopen()s the lib by path, then runs THAT host binary
# only if it dlopen()s a host-arch object — instead, we test the .so by
# inspecting its NEEDED entries with `readelf`/`file` because dlopen of
# a riscv64 .so from a qemu-riscv64-static-run binary would need the
# riscv64 libdl/libc loader chain present at the right sysroot path,
# which is not assumed available. The harness therefore does:
# 1. ELF arch check (must be UCB RISC-V).
# 2. readelf -d sanity (must be DYN, must have at least one NEEDED).
# This is the same verification ndk-stack and friends use for shipped
# .so artifacts where running them in isolation is meaningless.
verify_shared_lib() {
local so="$1"
if ! is_riscv64_elf "$so"; then
echo "FAIL|not a riscv64 ELF (file says: $(file -L -b "$so" 2>/dev/null | head -c 120))|0"
return
fi
if ! command -v readelf >/dev/null 2>&1; then
echo "PASS|ELF tag rv64 ok (readelf unavailable, skipped NEEDED check)|0"
return
fi
# readelf needs the real file (follow symlink with realpath if available).
local target="$so"
if [ -L "$so" ]; then
target="$(readlink -f "$so" 2>/dev/null || echo "$so")"
fi
local needed
needed="$(readelf -d "$target" 2>/dev/null | awk '/NEEDED/ {gsub(/[\[\]]/,"",$NF); print $NF}' | tr '\n' ',' | sed 's/,$//')"
if [ -z "$needed" ]; then
echo "PASS|ELF tag rv64 ok, no NEEDED (leaf .so)|0"
else
echo "PASS|ELF tag rv64 ok, NEEDED=[$needed]|0"
fi
}
verify_static_archive() {
local a="$1"
if ar_members_are_rv64 "$a"; then
echo "PASS|all .o members are UCB RISC-V|0"
else
echo "FAIL|one or more .o members are not UCB RISC-V|0"
fi
}
verify_artifact() {
# $1 = path. Determines kind by extension / executable bit.
local p="$1"
if [ ! -e "$p" ]; then
emit_record "$p" "missing" "SKIP" "artifact not built yet" "0"
return
fi
case "$p" in
*.a)
local result; result="$(verify_static_archive "$p")"
local status detail dur
status="${result%%|*}"; rest="${result#*|}"; detail="${rest%|*}"; dur="${rest##*|}"
emit_record "$p" "static-archive" "$status" "$detail" "$dur";;
*.so|*.so.*)
local result; result="$(verify_shared_lib "$p")"
local status detail dur
status="${result%%|*}"; rest="${result#*|}"; detail="${rest%|*}"; dur="${rest##*|}"
emit_record "$p" "shared-library" "$status" "$detail" "$dur";;
*)
if [ -f "$p" ] && is_riscv64_elf "$p"; then
if [ -x "$p" ] || head -c 4 "$p" | od -An -c 2>/dev/null | grep -q "\\\\177 E L F"; then
local result; result="$(run_executable_under_qemu "$p")"
local status detail dur
status="${result%%|*}"; rest="${result#*|}"; detail="${rest%|*}"; dur="${rest##*|}"
emit_record "$p" "executable" "$status" "$detail" "$dur"
else
emit_record "$p" "data" "SKIP" "ELF but not executable" "0"
fi
else
emit_record "$p" "unknown" "FAIL" "not a riscv64 ELF (file: $(file -L -b "$p" 2>/dev/null | head -c 120))" "0"
fi;;
esac
}
# ── Artifact inventory ───────────────────────────────────────────────
# Each entry's path is relative to repo root. Missing artifacts are
# reported as SKIP (with reason) — the build-driver script
# scripts/build-riscv64-artifacts.sh is responsible for producing them.
NATIVE_PLUGINS=(
"qjl-cpu packages/native/plugins/qjl-cpu/build/riscv64/libqjl.a"
"qjl-cpu packages/native/plugins/qjl-cpu/build/riscv64/qjl_int8_smoke"
"qjl-cpu packages/native/plugins/qjl-cpu/build/riscv64/qjl_avxvnni_smoke"
"qjl-cpu packages/native/plugins/qjl-cpu/build/riscv64/qjl_fork_parity"
"qjl-cpu packages/native/plugins/qjl-cpu/build/riscv64/qjl_bench"
"polarquant-cpu packages/native/plugins/polarquant-cpu/build/riscv64/libpolarquant.a"
"polarquant-cpu packages/native/plugins/polarquant-cpu/build/riscv64/polar_simd_parity_test"
"polarquant-cpu packages/native/plugins/polarquant-cpu/build/riscv64/polar_dot_test"
"polarquant-cpu packages/native/plugins/polarquant-cpu/build/riscv64/polar_preht_dot_test"
"polarquant-cpu packages/native/plugins/polarquant-cpu/build/riscv64/polar_preht_simd_parity_test"
"polarquant-cpu packages/native/plugins/polarquant-cpu/build/riscv64/polar_roundtrip_test"
"polarquant-cpu packages/native/plugins/polarquant-cpu/build/riscv64/polar_bench"
"turboquant-cpu packages/native/plugins/turboquant-cpu/build/riscv64/libturboquant.a"
"turboquant-cpu packages/native/plugins/turboquant-cpu/build/riscv64/turboquant_smoke"
"turboquant-cpu packages/native/plugins/turboquant-cpu/build/riscv64/turboquant_simd_parity"
"silero-vad-cpp packages/native/plugins/silero-vad-cpp/build/riscv64/libsilero_vad.a"
"silero-vad-cpp packages/native/plugins/silero-vad-cpp/build/riscv64/libsilero_vad.so"
"silero-vad-cpp packages/native/plugins/silero-vad-cpp/build/riscv64/silero_vad_abi_smoke"
"silero-vad-cpp packages/native/plugins/silero-vad-cpp/build/riscv64/silero_vad_resample_test"
"silero-vad-cpp packages/native/plugins/silero-vad-cpp/build/riscv64/silero_vad_runtime_test"
"silero-vad-cpp packages/native/plugins/silero-vad-cpp/build/riscv64/silero_vad_state_test"
"voice-classifier-cpp packages/native/plugins/voice-classifier-cpp/build/riscv64/libvoice_classifier.a"
"voice-classifier-cpp packages/native/plugins/voice-classifier-cpp/build/riscv64/libvoice_classifier.so"
"voice-classifier-cpp packages/native/plugins/voice-classifier-cpp/build/riscv64/voice_classifier_abi_smoke"
"voice-classifier-cpp packages/native/plugins/voice-classifier-cpp/build/riscv64/voice_diarizer_parity_test"
"voice-classifier-cpp packages/native/plugins/voice-classifier-cpp/build/riscv64/voice_emotion_classes_test"
"voice-classifier-cpp packages/native/plugins/voice-classifier-cpp/build/riscv64/voice_gguf_loader_test"
"voice-classifier-cpp packages/native/plugins/voice-classifier-cpp/build/riscv64/voice_mel_features_test"
"voice-classifier-cpp packages/native/plugins/voice-classifier-cpp/build/riscv64/voice_speaker_distance_test"
"voice-classifier-cpp packages/native/plugins/voice-classifier-cpp/build/riscv64/voice_speaker_parity_test"
"wakeword-cpp packages/native/plugins/wakeword-cpp/build/riscv64/libwakeword.a"
"wakeword-cpp packages/native/plugins/wakeword-cpp/build/riscv64/libwakeword.so"
"wakeword-cpp packages/native/plugins/wakeword-cpp/build/riscv64/wakeword_abi_smoke"
"wakeword-cpp packages/native/plugins/wakeword-cpp/build/riscv64/wakeword_melspec_test"
"wakeword-cpp packages/native/plugins/wakeword-cpp/build/riscv64/wakeword_runtime_test"
"wakeword-cpp packages/native/plugins/wakeword-cpp/build/riscv64/wakeword_window_test"
"yolo-cpp packages/native/plugins/yolo-cpp/build/riscv64/libyolo.a"
"yolo-cpp packages/native/plugins/yolo-cpp/build/riscv64/libyolo.so"
"yolo-cpp packages/native/plugins/yolo-cpp/build/riscv64/yolo_abi_smoke"
"yolo-cpp packages/native/plugins/yolo-cpp/build/riscv64/yolo_classes_test"
"yolo-cpp packages/native/plugins/yolo-cpp/build/riscv64/yolo_letterbox_test"
"yolo-cpp packages/native/plugins/yolo-cpp/build/riscv64/yolo_nms_test"
"yolo-cpp packages/native/plugins/yolo-cpp/build/riscv64/yolo_runtime_test"
"face-cpp packages/native/plugins/face-cpp/build/riscv64/libface.a"
"face-cpp packages/native/plugins/face-cpp/build/riscv64/libface.so"
"face-cpp packages/native/plugins/face-cpp/build/riscv64/face_abi_smoke"
"face-cpp packages/native/plugins/face-cpp/build/riscv64/face_align_test"
"face-cpp packages/native/plugins/face-cpp/build/riscv64/face_anchor_test"
"face-cpp packages/native/plugins/face-cpp/build/riscv64/face_distance_test"
"face-cpp packages/native/plugins/face-cpp/build/riscv64/face_embed_runtime_test"
"face-cpp packages/native/plugins/face-cpp/build/riscv64/face_runtime_test"
"doctr-cpp packages/native/plugins/doctr-cpp/build/riscv64/libdoctr.a"
"doctr-cpp packages/native/plugins/doctr-cpp/build/riscv64/doctr_abi_smoke"
)
# MTP libllama + ggml family stages into either:
# build/riscv64-stage/riscv64/ (linux-riscv64 staging)
# packages/app-core/platforms/android/app/src/main/jniLibs/riscv64/
# packages/app-core/platforms/android/app/src/main/assets/agent/riscv64/
# We probe all three locations; the harness reports SKIP only if none
# contains the .so.
LLAMA_FAMILY_BASENAMES=(
libllama.so
libggml.so
libggml-base.so
libggml-cpu.so
libllama-common.so
libeliza-llama-shim.so
)
LLAMA_FAMILY_SEARCH_DIRS=(
"build/riscv64-stage/riscv64"
"packages/app-core/platforms/android/app/src/main/jniLibs/riscv64"
"packages/app-core/platforms/android/app/src/main/assets/agent/riscv64"
)
SIGSYS_SEARCH=(
"${HOME}/.cache/eliza-android-agent/seccomp-shim/riscv64/libsigsys-handler.so"
)
# ── Walk + verify ────────────────────────────────────────────────────
echo "── Native plugins ──"
for entry in "${NATIVE_PLUGINS[@]}"; do
# shellcheck disable=SC2086
set -- $entry
pkg="$1"; path="$2"
verify_artifact "$repo_root/$path"
done
echo
echo "── libllama / libggml family (MTP) ──"
for basename in "${LLAMA_FAMILY_BASENAMES[@]}"; do
found=""
for dir in "${LLAMA_FAMILY_SEARCH_DIRS[@]}"; do
candidate="$repo_root/$dir/$basename"
if [ -e "$candidate" ]; then
found="$candidate"; break
fi
done
if [ -n "$found" ]; then
verify_artifact "$found"
else
# Synthesize a path for the report — caller knows what's missing.
emit_record "$repo_root/${LLAMA_FAMILY_SEARCH_DIRS[0]}/$basename" "shared-library" "SKIP" \
"not built; run \`bun run build:riscv64-artifacts\` (libllama for linux-riscv64-cpu + android-riscv64-cpu)" "0"
fi
done
echo
echo "── libsigsys-handler-riscv64 (Bun seccomp shim) ──"
s_found=""
for p in "${SIGSYS_SEARCH[@]}"; do
if [ -e "$p" ]; then s_found="$p"; break; fi
done
if [ -n "$s_found" ]; then
verify_artifact "$s_found"
else
emit_record "${SIGSYS_SEARCH[0]}" "shared-library" "SKIP" \
"not built; run \`node packages/app-core/scripts/aosp/compile-shim.mjs --abi riscv64\`" "0"
fi
# ── Verdict ──────────────────────────────────────────────────────────
if [ "$FAIL_N" -gt 0 ]; then
write_final_report "FAIL"
exit 1
fi
write_final_report "PASS"
exit 0