From 72d257e6026206364d6325069141986915da58a4 Mon Sep 17 00:00:00 2001 From: Parfii-bot Date: Fri, 1 May 2026 19:07:55 +0800 Subject: [PATCH] feat(install): Rust binary acquisition for fresh-clone installs (Path A + Path B) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-fix: install.sh on a fresh clone has no binaries — target/ is gitignored, copy_prebuilt_substrate_binaries() skips silently with "no pre-built found", end users get zero kei-fork / kei-ledger / kei-cortex / etc. New module: install/lib-rust-prebuild.sh (~120 LOC, Constructor Pattern). ensure_rust_binaries() — main entry, idempotent 1. has_prebuilt_substrate_binaries() — quorum check (5+ kit binaries already in target/release/ → no-op). 2. Else Path A: download keisei-${TARGET}.tar.gz from https://github.com/KeiSei84/KeiSeiKit-1.0/releases/latest/download/ Detects target via uname (x86_64/aarch64 × darwin/linux), verifies sha256, extracts into target/release/. 3. Path A fail (404, network, sha mismatch) → Path B fallback: cargo build --release --workspace (slow first time, requires Rust). 4. Path B fail (no cargo) → say + return non-zero. Bypass: KEI_SKIP_RUST=1 — skip both paths (markdown-only install). Wired in install/lib-rust.sh — ensure_rust_binaries() called BEFORE copy_prebuilt_substrate_binaries() in regenerate_rust_workspace. Path A activates ONLY after a v* tag is pushed and release.yml uploads tarballs to github releases. Until then, Path A 404s and Path B kicks in. This commit lays the wire — release tag is a separate user-driven action. Verify: - bash -n install.sh: OK - bash -n install/lib-rust-prebuild.sh: OK - detect_rust_target on this host: aarch64-apple-darwin - has_prebuilt_substrate_binaries: correctly returns false on partial dev target (only kei-memory + kei-db-contract built locally today) Out of scope (not done in this commit): - Tag v0.X release to populate github tarballs (deploy step, deferred) - Update keiseikit.dev/install.sh redirect target (downstream wiring) Co-Authored-By: Claude Opus 4.7 (1M context) --- install.sh | 2 + install/lib-rust-prebuild.sh | 125 +++++++++++++++++++++++++++++++++++ install/lib-rust.sh | 4 +- 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 install/lib-rust-prebuild.sh diff --git a/install.sh b/install.sh index 29ff670..dbc4f81 100755 --- a/install.sh +++ b/install.sh @@ -53,6 +53,8 @@ source "$LIB_DIR/lib-rust.sh" source "$LIB_DIR/lib-substrate.sh" # shellcheck source=install/lib-rust-mirror.sh source "$LIB_DIR/lib-rust-mirror.sh" +# shellcheck source=install/lib-rust-prebuild.sh +source "$LIB_DIR/lib-rust-prebuild.sh" # shellcheck source=install/lib-scaffold.sh source "$LIB_DIR/lib-scaffold.sh" # shellcheck source=install/lib-bridges.sh diff --git a/install/lib-rust-prebuild.sh b/install/lib-rust-prebuild.sh new file mode 100644 index 0000000..045876f --- /dev/null +++ b/install/lib-rust-prebuild.sh @@ -0,0 +1,125 @@ +# shellcheck shell=bash +# lib-rust-prebuild.sh — fresh-install Rust binary acquisition (v0.18+). +# +# Purpose: when install.sh runs in a fresh-clone tree, $KIT_DIR/_primitives/ +# _rust/target/release/ is empty (target/ is gitignored). Without binaries, +# copy_prebuilt_substrate_binaries() in lib-substrate.sh skips silently and +# end users get no kei-fork / kei-ledger / kei-cortex / etc. +# +# Two acquisition paths: +# +# Path A — download from latest github release (fast, no Rust required): +# 1. Detect platform via uname → Rust target triple. +# 2. Fetch keisei-${TARGET}.tar.gz from +# https://github.com/KeiSei84/KeiSeiKit-1.0/releases/latest/download/ +# 3. Verify sha256. +# 4. Extract into target/release/. +# +# Path B — cargo build --release --workspace fallback (slow, requires Rust): +# 1. Check `cargo` on PATH. +# 2. cd $KIT_DIR/_primitives/_rust && cargo build --release --workspace. +# 3. Slow first install (~5-15 min); subsequent installs are no-op. +# +# Path A tried first. On 404 / network fail / sha mismatch → Path B. +# On both failures → say + return non-zero (caller decides whether to abort). +# +# Requires: say from lib-log.sh. +# Reads globals: $KIT_DIR. + +# Detect Rust target triple for current host. +# Echo target triple. Echo nothing on unsupported platform. +detect_rust_target() { + local arch os + arch="$(uname -m)" + os="$(uname -s)" + case "$arch" in + x86_64|amd64) arch="x86_64" ;; + arm64|aarch64) arch="aarch64" ;; + *) say " unsupported arch: $arch" >&2; return 1 ;; + esac + case "$os" in + Darwin) echo "${arch}-apple-darwin" ;; + Linux) echo "${arch}-unknown-linux-gnu" ;; + *) say " unsupported os: $os" >&2; return 1 ;; + esac +} + +# True iff at least 5 of the kit's substrate-core binaries are pre-built +# in $KIT_DIR/_primitives/_rust/target/release/. Five is a quorum threshold; +# a partial build (e.g. user ran `cargo build -p kei-fork` once) is treated +# as "not pre-built" and we attempt full acquisition. +has_prebuilt_substrate_binaries() { + local src="$KIT_DIR/_primitives/_rust/target/release" + [ -d "$src" ] || return 1 + local count + count=$(ls -1 "$src" 2>/dev/null | grep -cE '^kei-(fork|ledger|spawn|memory|router|cortex|capability|pet|shared|store|task|search-core|migrate)$' || true) + [ "${count:-0}" -ge 5 ] +} + +# Path A: download release tarball from github. +# Returns 0 on success (binaries extracted), 1 on any failure (caller falls back). +download_release_tarball() { + local target="$1" + [ -n "$target" ] || return 1 + local tarball="keisei-${target}.tar.gz" + local url="https://github.com/KeiSei84/KeiSeiKit-1.0/releases/latest/download/${tarball}" + local tmp + tmp="$(mktemp -d -t keisei-prebuild-XXXX 2>/dev/null)" || return 1 + command -v curl >/dev/null 2>&1 || { rm -rf "$tmp"; return 1; } + say " downloading prebuilt ${tarball}…" + if ! curl -fL --max-time 120 -o "$tmp/$tarball" "$url" 2>/dev/null; then + say " ${tarball} not available (HTTP fail) — falling back to cargo build" + rm -rf "$tmp" + return 1 + fi + if curl -fL --max-time 30 -o "$tmp/${tarball}.sha256" "${url}.sha256" 2>/dev/null; then + (cd "$tmp" && shasum -a 256 -c "${tarball}.sha256" >/dev/null 2>&1) \ + || { say " sha256 mismatch on ${tarball} — refusing to install"; rm -rf "$tmp"; return 1; } + else + say " WARNING: no sha256 available for ${tarball}, proceeding without verification" + fi + local dst="$KIT_DIR/_primitives/_rust/target/release" + mkdir -p "$dst" || { rm -rf "$tmp"; return 1; } + if ! tar -xzf "$tmp/$tarball" -C "$dst" 2>/dev/null; then + say " failed to extract ${tarball}" + rm -rf "$tmp" + return 1 + fi + rm -rf "$tmp" + say " prebuilt binaries installed (Path A) ✓" + return 0 +} + +# Path B: cargo build --release --workspace fallback. +cargo_build_workspace_fallback() { + command -v cargo >/dev/null 2>&1 \ + || { say " cargo not on PATH — install Rust: https://rustup.rs/ (or set KEI_SKIP_RUST=1)"; return 1; } + local crates_dir="$KIT_DIR/_primitives/_rust" + [ -f "$crates_dir/Cargo.toml" ] || { say " $crates_dir/Cargo.toml missing"; return 1; } + say " cargo build --release --workspace (slow first time, ~5-15 min)" + (cd "$crates_dir" && cargo build --release --workspace 2>&1 | tail -3) || { + say " cargo build failed — see output above" + return 1 + } + say " binaries built from source (Path B) ✓" + return 0 +} + +# Main entry — call from install.sh BEFORE copy_prebuilt_substrate_binaries. +# Idempotent: returns 0 immediately if binaries already present. +ensure_rust_binaries() { + if has_prebuilt_substrate_binaries; then + return 0 + fi + if [ "${KEI_SKIP_RUST:-0}" = "1" ]; then + say " KEI_SKIP_RUST=1 — skipping Rust binary acquisition" + return 0 + fi + say "no prebuilt Rust binaries found — acquiring…" + local target + target="$(detect_rust_target)" || target="" + if [ -n "$target" ] && download_release_tarball "$target"; then + return 0 + fi + cargo_build_workspace_fallback +} diff --git a/install/lib-rust.sh b/install/lib-rust.sh index 21fb3da..51e304c 100644 --- a/install/lib-rust.sh +++ b/install/lib-rust.sh @@ -106,7 +106,8 @@ regenerate_rust_workspace() { members_nl="$(installed_rust_crates)" if [ -z "$members_nl" ]; then rm -f "$dst_root/Cargo.toml" "$dst_root/Cargo.lock" - copy_prebuilt_substrate_binaries + ensure_rust_binaries + copy_prebuilt_substrate_binaries return 0 fi local n @@ -119,6 +120,7 @@ regenerate_rust_workspace() { [ -n "$m" ] && [ -x "$dst_root/target/release/$m" ] && built=$((built+1)) done <<< "$members_nl" say " $built / $n Rust primitive binaries available" + ensure_rust_binaries copy_prebuilt_substrate_binaries }