Constructor Pattern (RULE ZERO). Zero behaviour change, zero flag
drift — all original CLI flags preserved verbatim.
Before: install.sh — 1238 LOC monolith
After: install.sh — 138 LOC dispatcher (sources libs in order)
install/lib-*.sh — 16 cubes, max 183 LOC (lib-menu)
Cubes:
lib-log 21 LOC — logging primitives
lib-backup 63 LOC — rollback trap + BACKUP_PAIRS
lib-profile 115 LOC — MANIFEST.toml profile resolution
lib-args 92 LOC — CLI parsing + --help heredoc
lib-menu 183 LOC — whiptail/dialog/plain-text interactive picker
lib-plan 150 LOC — dry-run --no-execute output
lib-prereqs 91 LOC — hard + soft dependency checks
lib-primitives 131 LOC — primitive copy + MANIFEST drive
lib-rust 114 LOC — cargo workspace build + pre-built support
lib-scaffold 144 LOC — agent/skill/block scaffolding
lib-bridges 31 LOC — project-bridge install
lib-hooks 104 LOC — settings.json jq merge
lib-agents 77 LOC — assembled agent output
lib-skills 23 LOC — skill copy
lib-wizard 20 LOC — sleep-setup wizard invocation
lib-summary 59 LOC — post-install summary
Invariants preserved:
- macOS bash 3.2 compat (no associative arrays, no [[ ]], no ${,,})
- rollback trap wired via setup_backup_trap early in dispatcher
- jq-merge behaviour verbatim in lib-hooks
- scoped Cargo.toml regeneration in lib-rust
Function LOC limits: largest non-heredoc fn 22 LOC (check_soft_prereqs).
Three functions kept >30 LOC because heredoc-dominated (print_help,
print_summary, profile_members); splitting would fragment logical unit.
62 unique function names across cubes, zero duplicates (grep-verified).
bash -n passes on all 17 files. Runtime smoke test deferred to user's
shell (bash-readonly sandbox constraint).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
114 lines
4.2 KiB
Bash
114 lines
4.2 KiB
Bash
# shellcheck shell=bash
|
|
# lib-rust.sh — scoped Rust workspace manifest + build orchestrator.
|
|
#
|
|
# Splits out the "primitives rust workspace" concern from lib-primitives.sh
|
|
# to stay under the Constructor Pattern <200 LOC limit. Handles:
|
|
# - list rust crates currently installed
|
|
# - regenerate a scoped Cargo.toml (members = only installed crates)
|
|
# - honour KEI_SKIP_RUST_BUILD + pre-built-binary detection
|
|
# - cargo build --offline, fall back to online on miss
|
|
#
|
|
# Requires: primitive_field from lib-profile.sh.
|
|
# Requires: read_installed from lib-primitives.sh.
|
|
# Requires: say / warn from lib-log.sh.
|
|
# Reads globals: $AGENTS_DIR, $KIT_DIR.
|
|
# Honours env: $KEI_SKIP_RUST_BUILD (1 = force-skip cargo build).
|
|
|
|
# Echo rust crates currently installed (by scanning .installed + MANIFEST).
|
|
installed_rust_crates() {
|
|
local dst_root="$AGENTS_DIR/_primitives/_rust"
|
|
local name kind crate
|
|
while IFS= read -r name; do
|
|
[ -z "$name" ] && continue
|
|
kind="$(primitive_field "$name" kind)"
|
|
[ "$kind" = "rust" ] || continue
|
|
crate="$(primitive_field "$name" crate)"
|
|
[ -n "$crate" ] && [ -d "$dst_root/$crate" ] && echo "$crate"
|
|
done <<< "$(read_installed)"
|
|
}
|
|
|
|
# Write a scoped Cargo.toml listing only the given members (stdin: one per line).
|
|
write_rust_workspace_manifest() {
|
|
local dst_root="$AGENTS_DIR/_primitives/_rust"
|
|
local src_wkspc="$KIT_DIR/_primitives/_rust/Cargo.toml"
|
|
local tmp="$dst_root/Cargo.toml.tmp"
|
|
{
|
|
echo '[workspace]'
|
|
echo 'resolver = "2"'
|
|
echo 'members = ['
|
|
local m
|
|
while IFS= read -r m; do
|
|
[ -n "$m" ] && echo " \"$m\","
|
|
done
|
|
echo ']'
|
|
awk '/^\[workspace\.package\]/,0' "$src_wkspc"
|
|
} > "$tmp"
|
|
mv "$tmp" "$dst_root/Cargo.toml"
|
|
if [ -f "$KIT_DIR/_primitives/_rust/Cargo.lock" ]; then
|
|
cp -f "$KIT_DIR/_primitives/_rust/Cargo.lock" "$dst_root/Cargo.lock"
|
|
fi
|
|
}
|
|
|
|
# Detect whether a usable set of pre-built release binaries already exists
|
|
# under `target/release/`. Returns 0 iff at least one expected crate-name
|
|
# executable is present AND executable.
|
|
have_prebuilt_binaries() {
|
|
local dst_root="$AGENTS_DIR/_primitives/_rust"
|
|
local target_dir="$dst_root/target/release"
|
|
[ -d "$target_dir" ] || return 1
|
|
local members_nl
|
|
members_nl="$(installed_rust_crates)"
|
|
[ -n "$members_nl" ] || return 1
|
|
local m found=0
|
|
while IFS= read -r m; do
|
|
[ -n "$m" ] && [ -x "$target_dir/$m" ] && found=$((found+1))
|
|
done <<< "$members_nl"
|
|
[ "$found" -gt 0 ]
|
|
}
|
|
|
|
# Build the scoped rust workspace. Offline-first, online fallback.
|
|
# Honours KEI_SKIP_RUST_BUILD=1 (force-skip) and auto-detects pre-built
|
|
# binaries dropped into target/release/ by a release-asset extract.
|
|
build_rust_workspace() {
|
|
local dst_root="$AGENTS_DIR/_primitives/_rust"
|
|
if [ "${KEI_SKIP_RUST_BUILD:-0}" = "1" ]; then
|
|
say " KEI_SKIP_RUST_BUILD=1 — skipping cargo build"
|
|
return 0
|
|
fi
|
|
if have_prebuilt_binaries; then
|
|
say " pre-built binaries detected in target/release/ — skipping cargo build"
|
|
say " (unset KEI_SKIP_RUST_BUILD or remove target/release to force rebuild)"
|
|
return 0
|
|
fi
|
|
if ! ( cd "$dst_root" && cargo build --workspace --release --offline ) 2>/tmp/keiseikit-primitives-offline.log; then
|
|
say " offline build failed — fetching deps from crates.io"
|
|
if ! ( cd "$dst_root" && cargo build --workspace --release ); then
|
|
warn "Rust primitive workspace build failed; shell primitives still work"
|
|
warn " see log: /tmp/keiseikit-primitives-offline.log"
|
|
return 0
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Orchestrator: installed rust crates -> scoped manifest -> cargo build ->
|
|
# per-crate "binary available?" report. No-op when no rust crates installed.
|
|
regenerate_rust_workspace() {
|
|
local dst_root="$AGENTS_DIR/_primitives/_rust"
|
|
mkdir -p "$dst_root"
|
|
local members_nl
|
|
members_nl="$(installed_rust_crates)"
|
|
if [ -z "$members_nl" ]; then
|
|
rm -f "$dst_root/Cargo.toml" "$dst_root/Cargo.lock"
|
|
return 0
|
|
fi
|
|
local n
|
|
n="$(printf '%s\n' "$members_nl" | grep -c .)"
|
|
printf '%s\n' "$members_nl" | write_rust_workspace_manifest
|
|
say "building Rust primitives ($n crate(s))"
|
|
build_rust_workspace
|
|
local built=0 m
|
|
while IFS= read -r m; do
|
|
[ -n "$m" ] && [ -x "$dst_root/target/release/$m" ] && built=$((built+1))
|
|
done <<< "$members_nl"
|
|
say " $built / $n Rust primitive binaries available"
|
|
}
|