diff --git a/architecture/custom-vm-runtime.md b/architecture/custom-vm-runtime.md index abb0aa6f..ce4d0bf3 100644 --- a/architecture/custom-vm-runtime.md +++ b/architecture/custom-vm-runtime.md @@ -122,11 +122,10 @@ graph LR subgraph Linux["Linux CI (build-libkrun.sh)"] BUILD_L["Build kernel + libkrunfw.so + libkrun.so"] - KERNELC["kernel.c\nKernel as C byte array"] end subgraph macOS["macOS CI (build-libkrun-macos.sh)"] - BUILD_M["Compile kernel.c -> libkrunfw.dylib\nBuild libkrun.dylib"] + BUILD_M["Build libkrunfw.dylib + libkrun.dylib"] end subgraph Output["target/libkrun-build/"] @@ -136,8 +135,7 @@ graph LR KCONF --> BUILD_L BUILD_L --> LIB_SO - BUILD_L --> KERNELC - KERNELC --> BUILD_M + KCONF --> BUILD_M BUILD_M --> LIB_DY ``` @@ -228,18 +226,15 @@ supported platforms. Runs on-demand or when the kernel config / pinned versions | Platform | Runner | Build Method | |----------|--------|-------------| -| Linux ARM64 | `build-arm64` (self-hosted) | Native `build-libkrun.sh` (also exports kernel.c) | +| Linux ARM64 | `build-arm64` (self-hosted) | Native `build-libkrun.sh` | | Linux x86_64 | `build-amd64` (self-hosted) | Native `build-libkrun.sh` | -| macOS ARM64 | `macos-latest-xlarge` (GitHub-hosted) | `build-libkrun-macos.sh --kernel-dir` (uses pre-built kernel.c from ARM64) | +| macOS ARM64 | `macos-latest-xlarge` (GitHub-hosted) | `build-libkrun-macos.sh` | Artifacts: `vm-runtime-{platform}.tar.zst` containing libkrun, libkrunfw, gvproxy, and provenance metadata. -The aarch64 Linux kernel is compiled once on the Linux ARM64 runner. The resulting -`kernel.c` (a C source file containing the kernel as a byte array) is passed to the -macOS job, which compiles it into `libkrunfw.dylib` with Apple's `cc`. This eliminates -the need for krunvm/Fedora VM and cuts macOS CI from ~45 min to ~5 min. The kernel -inside libkrunfw is always Linux regardless of host platform. +Each platform builds its own libkrunfw and libkrun natively. The kernel inside +libkrunfw is always Linux regardless of host platform. ### VM Binary (`release-vm-dev.yml`) diff --git a/crates/openshell-vm/runtime/README.md b/crates/openshell-vm/runtime/README.md index aec2dce9..f4398110 100644 --- a/crates/openshell-vm/runtime/README.md +++ b/crates/openshell-vm/runtime/README.md @@ -27,21 +27,20 @@ runtime/ ## Build Pipeline -The kernel is compiled on Linux CI runners. macOS reuses the pre-built `kernel.c` -artifact from the Linux ARM64 build — no krunvm or Fedora VM needed. +Each platform builds its own kernel and runtime natively. ``` -Linux ARM64: builds aarch64 kernel -> .so + exports kernel.c (parallel) -Linux AMD64: builds x86_64 kernel -> .so (parallel) -macOS ARM64: reuses aarch64 kernel.c -> .dylib (depends on ARM64) +Linux ARM64: builds aarch64 kernel -> .so (parallel) +Linux AMD64: builds x86_64 kernel -> .so (parallel) +macOS ARM64: builds aarch64 kernel -> .dylib ``` ### Build Scripts | Script | Platform | What it does | |--------|----------|-------------| -| `tasks/scripts/vm/build-libkrun.sh` | Linux | Builds libkrunfw + libkrun from source, exports kernel.c | -| `tasks/scripts/vm/build-libkrun-macos.sh` | macOS | Compiles pre-built kernel.c into .dylib, builds libkrun | +| `tasks/scripts/vm/build-libkrun.sh` | Linux | Builds libkrunfw + libkrun from source | +| `tasks/scripts/vm/build-libkrun-macos.sh` | macOS | Builds libkrunfw + libkrun from source | | `tasks/scripts/vm/package-vm-runtime.sh` | Any | Packages runtime tarball (libs + gvproxy + provenance) | ### Quick Build (Linux) @@ -56,14 +55,12 @@ FROM_SOURCE=1 mise run vm:setup ### Quick Build (macOS) -On macOS, you need a pre-built `kernel.c` from a Linux ARM64 build: - ```bash # Download pre-built runtime (recommended, ~30s): mise run vm:setup -# Or if you have kernel.c from a Linux build: -tasks/scripts/vm/build-libkrun-macos.sh --kernel-dir target/libkrun-build +# Or build from source: +FROM_SOURCE=1 mise run vm:setup ``` ### Output @@ -74,8 +71,6 @@ Build artifacts are placed in `target/libkrun-build/`: target/libkrun-build/ libkrun.so / libkrun.dylib # The VMM library libkrunfw.so* / libkrunfw.dylib # Kernel firmware library - kernel.c # Linux kernel as C byte array (Linux only) - ABI_VERSION # ABI version number (Linux only) ``` ## Networking diff --git a/crates/openshell-vm/runtime/kernel/openshell.kconfig b/crates/openshell-vm/runtime/kernel/openshell.kconfig index 068be8cc..b5f0330a 100644 --- a/crates/openshell-vm/runtime/kernel/openshell.kconfig +++ b/crates/openshell-vm/runtime/kernel/openshell.kconfig @@ -119,6 +119,10 @@ CONFIG_MEMCG=y # ── Disable kernel headers archive (avoids cpio issues in CI) ────────── # CONFIG_IKHEADERS is not set +# ── POSIX message queues (required by runc to mount /dev/mqueue in containers) ─ +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y + # ── Security features required by the sandbox runtime ─────────────────── CONFIG_SECURITY_LANDLOCK=y CONFIG_SECCOMP_FILTER=y diff --git a/crates/openshell-vm/src/lib.rs b/crates/openshell-vm/src/lib.rs index 9e44c996..2b78a766 100644 --- a/crates/openshell-vm/src/lib.rs +++ b/crates/openshell-vm/src/lib.rs @@ -61,6 +61,15 @@ pub enum VmError { #[error("host setup failed: {0}")] HostSetup(String), + /// `/dev/kvm` is not accessible (Linux only). + #[error( + "cannot open /dev/kvm: {reason}\n\ + KVM access is required to run microVMs on Linux.\n\ + Fix: sudo usermod -aG kvm $USER then log out and back in\n\ + (or run: newgrp kvm)" + )] + KvmAccess { reason: String }, + /// `fork()` failed. #[error("fork() failed: {0}")] Fork(String), @@ -1180,6 +1189,22 @@ fn path_to_cstring(path: &Path) -> Result { Ok(CString::new(s)?) } +/// Check that `/dev/kvm` is readable before attempting to boot. +/// +/// libkrun panics with an opaque Rust panic (instead of returning an error +/// code) when `/dev/kvm` is inaccessible. This pre-check turns that into a +/// clear, actionable error message. +#[cfg(target_os = "linux")] +fn check_kvm_access() -> Result<(), VmError> { + use std::fs::OpenOptions; + match OpenOptions::new().read(true).open("/dev/kvm") { + Ok(_) => Ok(()), + Err(e) => Err(VmError::KvmAccess { + reason: e.to_string(), + }), + } +} + // ── Launch ────────────────────────────────────────────────────────────── /// Configure and launch a libkrun microVM. @@ -1204,6 +1229,13 @@ pub fn launch(config: &VmConfig) -> Result { path: config.rootfs.display().to_string(), }); } + + // On Linux, libkrun uses KVM for hardware virtualization. Check access + // before starting so a missing kvm group membership produces a clear + // error instead of a cryptic panic inside krun_start_enter. + #[cfg(target_os = "linux")] + check_kvm_access()?; + if config.exec_path == "/srv/openshell-vm-init.sh" { ensure_vm_not_running(&config.rootfs)?; } diff --git a/deploy/docker/Dockerfile.images b/deploy/docker/Dockerfile.images index 4507125c..060c9b73 100644 --- a/deploy/docker/Dockerfile.images +++ b/deploy/docker/Dockerfile.images @@ -50,11 +50,12 @@ COPY crates/openshell-core/Cargo.toml crates/openshell-core/Cargo.toml COPY crates/openshell-ocsf/Cargo.toml crates/openshell-ocsf/Cargo.toml COPY crates/openshell-policy/Cargo.toml crates/openshell-policy/Cargo.toml COPY crates/openshell-providers/Cargo.toml crates/openshell-providers/Cargo.toml +COPY crates/openshell-prover/Cargo.toml crates/openshell-prover/Cargo.toml COPY crates/openshell-router/Cargo.toml crates/openshell-router/Cargo.toml COPY crates/openshell-sandbox/Cargo.toml crates/openshell-sandbox/Cargo.toml COPY crates/openshell-server/Cargo.toml crates/openshell-server/Cargo.toml COPY crates/openshell-tui/Cargo.toml crates/openshell-tui/Cargo.toml -COPY crates/openshell-prover/Cargo.toml crates/openshell-prover/Cargo.toml +COPY crates/openshell-vm/Cargo.toml crates/openshell-vm/Cargo.toml COPY crates/openshell-core/build.rs crates/openshell-core/build.rs COPY proto/ proto/ @@ -65,24 +66,27 @@ RUN mkdir -p \ crates/openshell-ocsf/src \ crates/openshell-policy/src \ crates/openshell-providers/src \ + crates/openshell-prover/src \ crates/openshell-router/src \ crates/openshell-sandbox/src \ crates/openshell-server/src \ - crates/openshell-prover/src \ - crates/openshell-tui/src && \ + crates/openshell-tui/src \ + crates/openshell-vm/src && \ touch crates/openshell-bootstrap/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-cli/src/main.rs && \ touch crates/openshell-core/src/lib.rs && \ touch crates/openshell-ocsf/src/lib.rs && \ touch crates/openshell-policy/src/lib.rs && \ touch crates/openshell-providers/src/lib.rs && \ + touch crates/openshell-prover/src/lib.rs && \ touch crates/openshell-router/src/lib.rs && \ touch crates/openshell-sandbox/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-sandbox/src/main.rs && \ touch crates/openshell-server/src/lib.rs && \ printf 'fn main() {}\n' > crates/openshell-server/src/main.rs && \ - touch crates/openshell-prover/src/lib.rs && \ - touch crates/openshell-tui/src/lib.rs + touch crates/openshell-tui/src/lib.rs && \ + touch crates/openshell-vm/src/lib.rs && \ + printf 'fn main() {}\n' > crates/openshell-vm/src/main.rs FROM rust-builder-skeleton AS rust-deps diff --git a/tasks/scripts/vm/build-libkrun.sh b/tasks/scripts/vm/build-libkrun.sh index 5b8b775b..9e2217f5 100755 --- a/tasks/scripts/vm/build-libkrun.sh +++ b/tasks/scripts/vm/build-libkrun.sh @@ -93,17 +93,33 @@ install_deps() { echo " libelf-dev libssl-dev bc curl cpio" >&2 fi - # Ensure pyelftools is importable by the Python that will run bin2cbundle.py. - # The apt package may install to a different Python than the default python3. - if ! python3 -c "import elftools" &>/dev/null; then - echo " pyelftools not importable, installing via pip..." - python3 -m pip install --break-system-packages pyelftools 2>/dev/null || \ - python3 -m pip install pyelftools || true - fi } install_deps +# libkrunfw's Makefile invokes `python3` from PATH for bin2cbundle.py. A mise shim, +# project venv, or other early PATH entry often shadows /usr/bin/python3 and does +# not ship pyelftools even when python3-pyelftools is installed for the distro. +ensure_python3_with_pyelftools_for_libkrunfw() { + echo " Checking Python 3 + pyelftools (libkrunfw bin2cbundle.py)..." + if python3 -c 'from elftools.elf.elffile import ELFFile' 2>/dev/null; then + echo " OK ($(command -v python3))" + return 0 + fi + if [ -x /usr/bin/python3 ] && /usr/bin/python3 -c 'from elftools.elf.elffile import ELFFile' 2>/dev/null; then + export PATH="/usr/bin:${PATH}" + echo " Using /usr/bin/python3 (PATH python3 lacked pyelftools; system Python has it)." + return 0 + fi + echo "ERROR: Python 3 with pyelftools is required to build libkrunfw (kernel.c generation)." >&2 + echo " Install: Debian/Ubuntu: sudo apt-get install -y python3-pyelftools" >&2 + echo " Fedora/RHEL: sudo dnf install -y python3-pyelftools" >&2 + echo " pip: python3 -m pip install --user pyelftools" >&2 + echo " If the package is installed but this still fails, PATH may point at another python3 (mise, venv)." >&2 + echo " Try: PATH=/usr/bin:\$PATH mise run vm:setup" >&2 + exit 1 +} + # ── Setup build directory ─────────────────────────────────────────────── mkdir -p "$BUILD_DIR" @@ -114,6 +130,8 @@ cd "$BUILD_DIR" echo "" echo "==> Building libkrunfw with custom kernel config..." +ensure_python3_with_pyelftools_for_libkrunfw + if [ ! -d libkrunfw ]; then echo " Cloning libkrunfw (pinned: ${LIBKRUNFW_REF:-HEAD})..." git clone https://github.com/containers/libkrunfw.git @@ -221,54 +239,168 @@ make -j"$(nproc)" cp libkrunfw.so* "$OUTPUT_DIR/" echo " Built: $(ls "$OUTPUT_DIR"/libkrunfw.so* | xargs -n1 basename | tr '\n' ' ')" -# ── Export kernel.c for cross-platform builds ─────────────────────────── -# kernel.c is a C source file containing the compiled Linux kernel as a byte -# array. It is architecture-specific (aarch64 vs x86_64) but OS-agnostic — -# any C compiler can turn it into a .so or .dylib. We export it so the macOS -# job can produce libkrunfw.dylib without rebuilding the kernel. +cd "$BUILD_DIR" -ABI_VERSION="$(grep -oE 'ABI_VERSION\s*=\s*[0-9]+' Makefile | head -1 | sed 's/[^0-9]//g')" +# ── Build libkrun (VMM) ───────────────────────────────────────────────── -if [ -f kernel.c ]; then - cp kernel.c "$OUTPUT_DIR/kernel.c" - echo "${ABI_VERSION}" > "$OUTPUT_DIR/ABI_VERSION" - echo " Exported kernel.c ($(du -sh kernel.c | cut -f1)) and ABI_VERSION=${ABI_VERSION}" -else - echo "Warning: kernel.c not found — cross-platform builds will not work" >&2 -fi +# libkrun's Makefile invokes plain `cargo`. Ubuntu/Debian often put /usr/bin/cargo +# (e.g. 1.75) ahead of mise/rustup; upstream requires edition 2024 (Cargo >= 1.85). +ensure_cargo_for_libkrun() { + local min_ver="${LIBKRUN_MIN_CARGO_VERSION:-1.85}" + local have ver_line bindir candidates_mise candidates_home + + cargo_meets_min() { + local bin="$1" + local v + [ -x "$bin" ] || return 1 + v="$("$bin" --version 2>/dev/null | awk '{print $2}')" + [ -n "$v" ] || return 1 + [ "$(printf '%s\n' "${min_ver}" "$v" | sort -V | head -n1)" = "${min_ver}" ] + } + + echo " Checking Cargo (libkrun needs >= ${min_ver}, edition 2024)..." + if cargo_meets_min "$(command -v cargo 2>/dev/null || true)"; then + echo " OK ($(command -v cargo) — $(cargo --version))" + return 0 + fi -cd "$BUILD_DIR" + candidates_mise="" + if command -v mise &>/dev/null; then + if ver_line="$(mise which cargo 2>/dev/null)" && [ -n "${ver_line}" ]; then + candidates_mise="$(dirname "${ver_line}")" + fi + fi + candidates_home="${HOME}/.cargo/bin" + + for bindir in "${candidates_mise}" "${candidates_home}"; do + [ -n "${bindir}" ] || continue + if cargo_meets_min "${bindir}/cargo"; then + export PATH="${bindir}:${PATH}" + echo " Using ${bindir}/cargo ($("${bindir}/cargo" --version))" + return 0 + fi + done -# ── Build libkrun (VMM) ───────────────────────────────────────────────── + echo "ERROR: Cargo >= ${min_ver} is required to build libkrun (Rust edition 2024)." >&2 + echo " Current: $(command -v cargo 2>/dev/null || echo '(no cargo in PATH)') $(cargo --version 2>/dev/null || true)" >&2 + echo " Typical fix: run vm:setup via mise from the repo so Rust stable is on PATH," >&2 + echo " or: rustup update stable && export PATH=\"\$HOME/.cargo/bin:\$PATH\"" >&2 + echo " Override minimum: LIBKRUN_MIN_CARGO_VERSION=…" >&2 + exit 1 +} + +# Directory must contain libclang.so or libclang-.so (what clang-sys expects +# for linking; bare .so.N sonames alone are not enough). +_libclang_dir_usable() { + local d="$1" + [ -n "$d" ] && [ -d "$d" ] || return 1 + if [ -e "$d/libclang.so" ]; then + return 0 + fi + local f base + for f in "$d"/libclang-*.so; do + [ -e "$f" ] || continue + base="$(basename "$f")" + case "$base" in + *-cpp.so*) continue ;; + esac + if [[ "$base" == libclang-*.so ]] && [[ "$base" != *.so.[0-9]* ]]; then + return 0 + fi + done + return 1 +} + +ensure_libclang_for_libkrun() { + local user_libclang="${LIBCLANG_PATH:-}" + + if [ -n "$user_libclang" ] && _libclang_dir_usable "$user_libclang"; then + export LIBCLANG_PATH="$user_libclang" + echo " LIBCLANG_PATH=$LIBCLANG_PATH (from environment)" + return 0 + fi + + if [ -n "$user_libclang" ]; then + echo " Warning: LIBCLANG_PATH='$user_libclang' has no libclang.so or libclang-*.so symlink;" >&2 + echo " those are required for clang-sys. Searching other system locations..." >&2 + fi + unset LIBCLANG_PATH + + local llvm_lib + if command -v llvm-config &>/dev/null; then + llvm_lib="$(llvm-config --libdir 2>/dev/null)" || true + if [ -n "${llvm_lib}" ] && _libclang_dir_usable "$llvm_lib"; then + export LIBCLANG_PATH="$llvm_lib" + echo " LIBCLANG_PATH=$LIBCLANG_PATH (from llvm-config --libdir)" + return 0 + fi + fi + + shopt -s nullglob + local candidates=(/usr/lib/llvm-*/lib) + shopt -u nullglob + while IFS= read -r llvm_lib; do + [ -n "$llvm_lib" ] || continue + if _libclang_dir_usable "$llvm_lib"; then + export LIBCLANG_PATH="$llvm_lib" + echo " LIBCLANG_PATH=$LIBCLANG_PATH (from /usr/lib/llvm-*/lib)" + return 0 + fi + done < <(printf '%s\n' "${candidates[@]}" | sort -rV) + + local multi + multi="$(gcc -print-multiarch 2>/dev/null || true)" + if [ -n "$multi" ] && _libclang_dir_usable "/usr/lib/${multi}"; then + export LIBCLANG_PATH="/usr/lib/${multi}" + echo " LIBCLANG_PATH=$LIBCLANG_PATH (from gcc multiarch /usr/lib/${multi})" + return 0 + fi + + if _libclang_dir_usable "/usr/lib64"; then + export LIBCLANG_PATH="/usr/lib64" + echo " LIBCLANG_PATH=$LIBCLANG_PATH (from /usr/lib64)" + return 0 + fi + + echo "ERROR: libclang is required to build libkrun (Rust bindgen / clang-sys) but was not found." >&2 + if [ -n "$user_libclang" ]; then + echo " You had LIBCLANG_PATH='$user_libclang' (ignored after search failed)." >&2 + fi + echo " Install LLVM/Clang development packages, then re-run vm:setup:" >&2 + echo " Debian/Ubuntu: sudo apt-get install -y libclang-dev" >&2 + echo " Fedora/RHEL: sudo dnf install -y clang-devel" >&2 + echo " Then unset LIBCLANG_PATH or set it to a directory that contains libclang.so." >&2 + exit 1 +} echo "" echo "==> Building libkrun..." +ensure_cargo_for_libkrun +ensure_libclang_for_libkrun + +LIBKRUN_REF="${LIBKRUN_REF:-v1.17.4}" + if [ ! -d libkrun ]; then echo " Cloning libkrun..." - git clone --depth 1 https://github.com/containers/libkrun.git + git clone https://github.com/containers/libkrun.git fi cd libkrun -# Build with NET support for gvproxy networking and BLK support for the -# host-backed state disk. -echo " Building libkrun with NET=1 BLK=1..." +if [ -n "${LIBKRUN_REF:-}" ]; then + echo " Checking out pinned ref: ${LIBKRUN_REF}" + git fetch origin "${LIBKRUN_REF}" 2>/dev/null || git fetch origin + git checkout "${LIBKRUN_REF}" 2>/dev/null || git checkout "origin/${LIBKRUN_REF}" 2>/dev/null || true +fi -# Locate libclang for clang-sys if LIBCLANG_PATH isn't already set. -# clang-sys looks for libclang.so or libclang-*.so; on Debian/Ubuntu the -# versioned file (e.g. libclang-18.so.18) lives under the LLVM lib dir. -if [ -z "${LIBCLANG_PATH:-}" ]; then - for llvm_lib in /usr/lib/llvm-*/lib; do - if ls "$llvm_lib"/libclang*.so* &>/dev/null; then - export LIBCLANG_PATH="$llvm_lib" - echo " LIBCLANG_PATH=$LIBCLANG_PATH" - break - fi - done +if [ -f init/Makefile ] || grep -q 'init/init' Makefile 2>/dev/null; then + echo " Building init/init binary..." + make init/init fi -make NET=1 BLK=1 -j"$(nproc)" +echo " Building libkrun with NET=1 BLK=1..." +cargo build --release --features blk --features net --target-dir="$(pwd)/target" # Copy output cp target/release/libkrun.so "$OUTPUT_DIR/" @@ -283,7 +415,6 @@ echo "==> Build complete!" echo " Output directory: ${OUTPUT_DIR}" echo "" echo " Artifacts:" -ls -lah "$OUTPUT_DIR"/*.so* "$OUTPUT_DIR"/kernel.c "$OUTPUT_DIR"/ABI_VERSION 2>/dev/null || \ ls -lah "$OUTPUT_DIR"/*.so* echo "" diff --git a/tasks/scripts/vm/compress-vm-runtime.sh b/tasks/scripts/vm/compress-vm-runtime.sh index 67290a93..efada8a2 100755 --- a/tasks/scripts/vm/compress-vm-runtime.sh +++ b/tasks/scripts/vm/compress-vm-runtime.sh @@ -55,8 +55,57 @@ make_dylib_portable() { WORK_DIR="${ROOT}/target/vm-runtime" OUTPUT_DIR="${OPENSHELL_VM_RUNTIME_COMPRESSED_DIR:-${ROOT}/target/vm-runtime-compressed}" +mkdir -p "$OUTPUT_DIR" + +# ── Fast path: compressed artifacts already present (e.g. from vm:setup) ── + +_check_compressed_artifacts() { + local dir="$1" + local platform + platform="$(uname -s)-$(uname -m)" + case "$platform" in + Darwin-arm64) + for f in libkrun.dylib.zst libkrunfw.5.dylib.zst gvproxy.zst; do + [ -f "${dir}/${f}" ] || return 1 + done + ;; + Linux-*) + for f in libkrun.so.zst libkrunfw.so.5.zst gvproxy.zst; do + [ -f "${dir}/${f}" ] || return 1 + done + ;; + *) return 1 ;; + esac + return 0 +} + +if [ -z "${VM_RUNTIME_TARBALL:-}" ] && _check_compressed_artifacts "$OUTPUT_DIR"; then + echo "==> Compressed artifacts already present in ${OUTPUT_DIR} — skipping compression." + ls -lah "$OUTPUT_DIR" + + # Decompress artifacts into WORK_DIR so bundle-vm-runtime.sh can find them. + echo "" + echo "==> Decompressing artifacts into ${WORK_DIR} for runtime bundle..." + rm -rf "$WORK_DIR" + mkdir -p "$WORK_DIR" + for f in "${OUTPUT_DIR}"/*.zst; do + [ -f "$f" ] || continue + name="$(basename "${f%.zst}")" + # Skip rootfs tarball — bundle-vm-runtime.sh doesn't need it + [[ "$name" == rootfs.tar ]] && continue + zstd -d "$f" -o "${WORK_DIR}/${name}" -f -q + chmod 0755 "${WORK_DIR}/${name}" + done + echo " Decompressed files:" + ls -lah "$WORK_DIR" + + echo "" + echo "Next step: cargo build -p openshell-vm" + exit 0 +fi + rm -rf "$WORK_DIR" -mkdir -p "$WORK_DIR" "$OUTPUT_DIR" +mkdir -p "$WORK_DIR" # ── Fast path: pre-built tarball from CI or download-kernel-runtime.sh ── diff --git a/tasks/scripts/vm/sync-vm-rootfs.sh b/tasks/scripts/vm/sync-vm-rootfs.sh index fa13ee1e..727a9dd1 100755 --- a/tasks/scripts/vm/sync-vm-rootfs.sh +++ b/tasks/scripts/vm/sync-vm-rootfs.sh @@ -104,19 +104,23 @@ done HELM_CHART_DIR="${ROOT}/deploy/helm/openshell" CHART_STAGING="${ROOTFS_DIR}/opt/openshell/charts" if [ -d "${HELM_CHART_DIR}" ]; then - mkdir -p "${CHART_STAGING}" - # Package into a temp dir and compare — only update if changed. - TMP_CHART=$(mktemp -d) - helm package "${HELM_CHART_DIR}" -d "${TMP_CHART}" >/dev/null 2>&1 - for tgz in "${TMP_CHART}"/*.tgz; do - [ -f "$tgz" ] || continue - base=$(basename "$tgz") - if ! cmp -s "$tgz" "${CHART_STAGING}/${base}" 2>/dev/null; then - cp "$tgz" "${CHART_STAGING}/${base}" - echo " updated: /opt/openshell/charts/${base}" - fi - done - rm -rf "${TMP_CHART}" + if ! command -v helm >/dev/null 2>&1; then + echo " warning: helm not found — skipping chart sync (run: mise install)" >&2 + else + mkdir -p "${CHART_STAGING}" + # Package into a temp dir and compare — only update if changed. + TMP_CHART=$(mktemp -d) + helm package "${HELM_CHART_DIR}" -d "${TMP_CHART}" >/dev/null 2>&1 + for tgz in "${TMP_CHART}"/*.tgz; do + [ -f "$tgz" ] || continue + base=$(basename "$tgz") + if ! cmp -s "$tgz" "${CHART_STAGING}/${base}" 2>/dev/null; then + cp "$tgz" "${CHART_STAGING}/${base}" + echo " updated: /opt/openshell/charts/${base}" + fi + done + rm -rf "${TMP_CHART}" + fi fi # ── Kubernetes manifests ───────────────────────────────────────────────