Files
OpenShell/scripts/bin/openshell
T
Drew Newberry 6ccc5cc993 feat(vm): add navigator-vm crate with libkrun microVM and k3s gateway
Add a new navigator-vm library crate that boots k3s inside a libkrun
microVM, accessible from the host via gvproxy port forwarding.

Key components:
- FFI bindings to libkrun C API (krun_create_ctx, krun_add_net_unixgram, etc.)
- VmConfig with gateway() preset for k3s and custom exec mode
- gvproxy integration: virtio-net via unixgram, DHCP, native HTTP port forwarding
- gateway-init.sh: PID 1 init script with DHCP via udhcpc, mounts, k3s exec
- build-rootfs.sh: builds Ubuntu 22.04 arm64 rootfs with k3s + busybox-static
- Kubeconfig auto-extraction to ~/.kube/gateway.yaml
- CLI integration as 'ncl gateway' with --exec, --port, --net flags
- macOS codesigning and DYLD_FALLBACK_LIBRARY_PATH in ncl wrapper
2026-03-16 10:41:53 -07:00

159 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
BINARY="$PROJECT_ROOT/target/debug/openshell"
STATE_FILE="$PROJECT_ROOT/.cache/openshell-build.state"
# ---------------------------------------------------------------------------
# Fingerprint-based rebuild check
#
# Mirrors the approach in tasks/scripts/cluster-deploy-fast.sh: collect dirty
# files from git, filter to paths in the openshell-cli dependency closure,
# hash their contents, and compare against a persisted state file. We also
# track HEAD so that branch switches / pulls are detected.
# ---------------------------------------------------------------------------
needs_build=0
if [[ ! -x "$BINARY" ]]; then
needs_build=1
else
cd "$PROJECT_ROOT"
# Current HEAD commit (detects branch switches, pulls, rebases)
current_head=$(git rev-parse HEAD 2>/dev/null || echo "unknown")
# Collect dirty (modified, staged, untracked) files
mapfile -t changed_files < <(
{
git diff --name-only 2>/dev/null
git diff --name-only --cached 2>/dev/null
git ls-files --others --exclude-standard 2>/dev/null
} | sort -u
)
# Filter to paths that can affect the openshell-cli binary
matches_cli() {
local path=$1
case "$path" in
Cargo.toml|Cargo.lock|proto/*)
return 0 ;;
crates/openshell-cli/*|crates/openshell-core/*|crates/openshell-bootstrap/*)
return 0 ;;
crates/openshell-policy/*|crates/openshell-providers/*|crates/openshell-tui/*|crates/openshell-vm/*)
return 0 ;;
*)
return 1 ;;
esac
}
# Compute a composite fingerprint of matching dirty files
payload=""
for path in "${changed_files[@]}"; do
matches_cli "$path" || continue
if [[ -e "$path" ]]; then
digest=$(shasum -a 256 "$path" | cut -d ' ' -f 1)
else
digest="__MISSING__"
fi
payload+="${path}:${digest}"$'\n'
done
if [[ -n "$payload" ]]; then
current_fingerprint=$(printf '%s' "$payload" | shasum -a 256 | cut -d ' ' -f 1)
else
current_fingerprint=""
fi
# Load previous state
previous_head=""
previous_fingerprint=""
if [[ -f "$STATE_FILE" ]]; then
while IFS='=' read -r key value; do
case "$key" in
head) previous_head=$value ;;
fingerprint) previous_fingerprint=$value ;;
esac
done < "$STATE_FILE"
fi
# Decide whether to rebuild
if [[ "$current_head" != "$previous_head" ]]; then
needs_build=1
elif [[ "$current_fingerprint" != "$previous_fingerprint" ]]; then
needs_build=1
fi
fi
if [[ "$needs_build" == "1" ]]; then
echo "Recompiling openshell..." >&2
cargo build --package openshell-cli --quiet
# On macOS, codesign with the hypervisor entitlement so libkrun can use
# Apple's Hypervisor.framework. Re-sign after every build.
ENTITLEMENTS="$PROJECT_ROOT/crates/openshell-vm/entitlements.plist"
if [[ "$(uname)" == "Darwin" ]] && [[ -f "$ENTITLEMENTS" ]]; then
codesign --entitlements "$ENTITLEMENTS" --force -s - "$BINARY" 2>/dev/null
fi
# Persist state after successful build
mkdir -p "$(dirname "$STATE_FILE")"
cd "$PROJECT_ROOT"
new_head=$(git rev-parse HEAD 2>/dev/null || echo "unknown")
# Recompute fingerprint of remaining dirty files (build may not change them)
mapfile -t post_files < <(
{
git diff --name-only 2>/dev/null
git diff --name-only --cached 2>/dev/null
git ls-files --others --exclude-standard 2>/dev/null
} | sort -u
)
matches_cli() {
local path=$1
case "$path" in
Cargo.toml|Cargo.lock|proto/*)
return 0 ;;
crates/openshell-cli/*|crates/openshell-core/*|crates/openshell-bootstrap/*)
return 0 ;;
crates/openshell-policy/*|crates/openshell-providers/*|crates/openshell-tui/*|crates/openshell-vm/*)
return 0 ;;
*)
return 1 ;;
esac
}
post_payload=""
for path in "${post_files[@]}"; do
matches_cli "$path" || continue
if [[ -e "$path" ]]; then
digest=$(shasum -a 256 "$path" | cut -d ' ' -f 1)
else
digest="__MISSING__"
fi
post_payload+="${path}:${digest}"$'\n'
done
if [[ -n "$post_payload" ]]; then
new_fingerprint=$(printf '%s' "$post_payload" | shasum -a 256 | cut -d ' ' -f 1)
else
new_fingerprint=""
fi
cat > "$STATE_FILE" <<EOF
head=${new_head}
fingerprint=${new_fingerprint}
EOF
fi
# Ensure libkrunfw is discoverable by libkrun's dlopen on macOS.
# dyld only reads DYLD_FALLBACK_LIBRARY_PATH at process startup, so we
# set it here before exec.
if [[ "$(uname)" == "Darwin" ]]; then
HOMEBREW_LIB="$(brew --prefix 2>/dev/null || echo /opt/homebrew)/lib"
export DYLD_FALLBACK_LIBRARY_PATH="${HOMEBREW_LIB}${DYLD_FALLBACK_LIBRARY_PATH:+:$DYLD_FALLBACK_LIBRARY_PATH}"
fi
exec "$BINARY" "$@"