KeiSeiKit-1.0/install/lib-primitives.sh
Parfii-bot 340de0e2f6 fix(install): copy sibling data dirs (schemas/ assets/ templates/ fixtures/ migrations/) in copy_rust_primitive
Ship-blocker found by Docker battle-test: install.sh --profile=full
only built 6/25 Rust binaries on fresh ubuntu:24.04 because
kei-artifact has sibling schemas/*.json files that src/schemas.rs
include_str!s at compile time. Prior copy_rust_primitive only copied
Cargo.toml + src/ + tests/ — schemas dir missing → 5 compile errors.

Fix: whitelist 5 common sibling data directories (schemas, assets,
templates, fixtures, migrations) — copy each if present. Keeps
target/, .git/, and other build artifacts out.

Battle-test (pre-fix):
  full profile: 6/25 Rust binaries built (kei-artifact + cascade fails)
  install exit 0 (soft-fail design), silent partial-install

Expected post-fix:
  full profile: 25/25 binaries (pending re-verification on Docker)

Affects: any future crate that uses include_str!('../<dir>/*') on
a sibling data folder. 5-item whitelist covers the patterns we know;
extend as needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 18:39:28 +08:00

140 lines
4.8 KiB
Bash

# shellcheck shell=bash
# lib-primitives.sh — shell-primitive copy + Rust workspace scoped build +
# .installed state helpers + --list printer.
#
# Requires: primitive_field from lib-profile.sh.
# Requires: say / warn / err from lib-log.sh.
# Reads globals: $AGENTS_DIR, $KIT_DIR, $INSTALLED_FILE, $MANIFEST.
# --- .installed state helpers --------------------------------------------
read_installed() {
[ -f "$INSTALLED_FILE" ] && cat "$INSTALLED_FILE" || true
}
write_installed() {
# stdin = newline-separated names; writes sorted-unique to INSTALLED_FILE.
mkdir -p "$(dirname "$INSTALLED_FILE")"
sort -u > "$INSTALLED_FILE"
}
# --- per-primitive install/remove ----------------------------------------
copy_shell_primitive() {
local name="$1" file src dst
file="$(primitive_field "$name" file)"
[ -n "$file" ] || { err "no 'file' for shell primitive $name"; return 1; }
src="$KIT_DIR/_primitives/$file"
dst="$AGENTS_DIR/_primitives/$file"
[ -f "$src" ] || { err "source missing: $src"; return 1; }
mkdir -p "$AGENTS_DIR/_primitives"
cp -f "$src" "$dst"
chmod +x "$dst"
say " + shell: $name ($file)"
}
remove_shell_primitive() {
local name="$1" file
file="$(primitive_field "$name" file)"
[ -n "$file" ] || return 0
rm -f "$AGENTS_DIR/_primitives/$file"
say " - shell: $name ($file)"
}
copy_rust_primitive() {
local name="$1" crate src dst_root dst sibling
crate="$(primitive_field "$name" crate)"
[ -n "$crate" ] || { err "no 'crate' for rust primitive $name"; return 1; }
src="$KIT_DIR/_primitives/_rust/$crate"
[ -d "$src" ] || { err "source missing: $src"; return 1; }
dst_root="$AGENTS_DIR/_primitives/_rust"
dst="$dst_root/$crate"
mkdir -p "$dst/src"
cp -f "$src/Cargo.toml" "$dst/Cargo.toml"
[ -d "$src/src" ] && cp -rf "$src/src/"* "$dst/src/" 2>/dev/null || true
if [ -d "$src/tests" ]; then
mkdir -p "$dst/tests"
cp -rf "$src/tests/"* "$dst/tests/" 2>/dev/null || true
fi
# v0.21: kei-artifact and future crates reference sibling data dirs via
# include_str!("../schemas/*.json") etc. Copy known sibling directories
# when present. Whitelist keeps target/, .git/, build artifacts out.
for sibling in schemas assets templates fixtures migrations; do
if [ -d "$src/$sibling" ]; then
mkdir -p "$dst/$sibling"
cp -rf "$src/$sibling/"* "$dst/$sibling/" 2>/dev/null || true
fi
done
say " + rust: $name (crate $crate)"
}
remove_rust_primitive() {
local name="$1" crate
crate="$(primitive_field "$name" crate)"
[ -n "$crate" ] || return 0
rm -rf "$AGENTS_DIR/_primitives/_rust/$crate"
say " - rust: $name (crate $crate)"
}
# --- rust enumeration / manifest / build all live in install/lib-rust.sh
# (Constructor-Pattern split — keeps this cube under 200 LOC).
# --- install / remove orchestrators --------------------------------------
# Install primitives from a name list (newline-separated on stdin).
install_primitives() {
local names existing combined kind p any_rust=0
names="$(cat)"
existing="$(read_installed)"
combined="$(printf '%s\n%s\n' "$existing" "$names" | grep -v '^$' || true)"
while IFS= read -r p; do
[ -z "$p" ] && continue
kind="$(primitive_field "$p" kind)"
case "$kind" in
shell) copy_shell_primitive "$p" ;;
rust) copy_rust_primitive "$p"; any_rust=1 ;;
*) warn "unknown primitive: $p (skipping)"; continue ;;
esac
done <<< "$names"
printf '%s\n' "$combined" | write_installed
if [ "$any_rust" = "1" ]; then
regenerate_rust_workspace
fi
}
# Remove a single primitive by name.
remove_primitive() {
local name="$1" kind existing
kind="$(primitive_field "$name" kind)"
case "$kind" in
shell) remove_shell_primitive "$name" ;;
rust) remove_rust_primitive "$name" ;;
*) err "unknown primitive: $name"; return 1 ;;
esac
existing="$(read_installed)"
printf '%s\n' "$existing" | grep -vFx "$name" | grep -v '^$' | write_installed || true
if [ "$kind" = "rust" ]; then
regenerate_rust_workspace
fi
}
# --- --list implementation -----------------------------------------------
cmd_list() {
echo
printf '%-22s %-6s %-10s %s\n' "NAME" "KIND" "STATUS" "DESCRIPTION"
printf '%-22s %-6s %-10s %s\n' "----" "----" "------" "-----------"
local installed name kind desc status count
installed="$(read_installed)"
while IFS= read -r name; do
[ -z "$name" ] && continue
kind="$(primitive_field "$name" kind)"
desc="$(primitive_field "$name" desc)"
if printf '%s\n' "$installed" | grep -qFx "$name"; then
status="INSTALLED"
else
status="-"
fi
printf '%-22s %-6s %-10s %s\n' "$name" "$kind" "$status" "$desc"
done < <(all_primitive_names)
echo
count="$(printf '%s\n' "$installed" | grep -c . || true)"
printf '%s primitives installed (state: %s)\n' "${count:-0}" "$INSTALLED_FILE"
echo
}