diff --git a/scripts/install.sh b/scripts/install.sh index 5b9f8d68112..bb6332e6ae6 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1189,6 +1189,7 @@ main() { _INSTALL_START=$SECONDS print_banner + bash "${SCRIPT_DIR}/setup-jetson.sh" step 1 "Node.js" install_nodejs diff --git a/scripts/setup-jetson.sh b/scripts/setup-jetson.sh new file mode 100755 index 00000000000..523d5ba8d77 --- /dev/null +++ b/scripts/setup-jetson.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +SUDO=() +((EUID != 0)) && SUDO=(sudo) + +info() { + printf "[INFO] %s\n" "$*" +} + +error() { + printf "[ERROR] %s\n" "$*" >&2 + exit 1 +} + +get_jetpack_version() { + local release_line release revision l4t_version + + release_line="$(head -n1 /etc/nv_tegra_release 2>/dev/null || true)" + [[ -n "$release_line" ]] || return 0 + + release="$(printf '%s\n' "$release_line" | sed -n 's/^# R\([0-9][0-9]*\) (release).*/\1/p')" + revision="$(printf '%s\n' "$release_line" | sed -n 's/^.*REVISION: \([0-9][0-9]*\)\..*$/\1/p')" + l4t_version="${release}.${revision}" + + case "$l4t_version" in + 36.*) + printf "%s" "jp6" + ;; + 38.*) + printf "%s" "jp7" + ;; + *) + info "Jetson detected (L4T $l4t_version) but version is not recognized — skipping host setup" + ;; + esac +} + +configure_jetson_host() { + local jetpack_version="$1" + + if ((EUID != 0)); then + info "Jetson host configuration requires sudo. You may be prompted for your password." + "${SUDO[@]}" true >/dev/null || error "Sudo is required to apply Jetson host configuration." + fi + + case "$jetpack_version" in + jp6) + "${SUDO[@]}" update-alternatives --set iptables /usr/sbin/iptables-legacy + "${SUDO[@]}" sed -i '/"iptables": false,/d; /"bridge": "none"/d; s/"default-runtime": "nvidia",/"default-runtime": "nvidia"/' /etc/docker/daemon.json + ;; + jp7) + # JP7 (Thor) does not need iptables or Docker daemon.json changes. + ;; + *) + error "Unsupported Jetson version: $jetpack_version" + ;; + esac + + "${SUDO[@]}" modprobe br_netfilter + "${SUDO[@]}" sysctl -w net.bridge.bridge-nf-call-iptables=1 >/dev/null + + # Persist across reboots + echo "br_netfilter" | "${SUDO[@]}" tee /etc/modules-load.d/nemoclaw.conf >/dev/null + echo "net.bridge.bridge-nf-call-iptables=1" | "${SUDO[@]}" tee /etc/sysctl.d/99-nemoclaw.conf >/dev/null + + if [[ "$jetpack_version" == "jp6" ]]; then + "${SUDO[@]}" systemctl restart docker + fi +} + +main() { + local jetpack_version + jetpack_version="$(get_jetpack_version)" + [[ -n "$jetpack_version" ]] || exit 0 + + info "Jetson detected ($jetpack_version) — applying required host configuration" + configure_jetson_host "$jetpack_version" +} + +main "$@" diff --git a/test/runner.test.ts b/test/runner.test.ts index 692a510e610..a4a444e7b65 100644 --- a/test/runner.test.ts +++ b/test/runner.test.ts @@ -629,6 +629,13 @@ describe("regression guards", () => { ); }); + it("scripts/setup-jetson.sh exists and is executable", () => { + const scriptPath = path.join(import.meta.dirname, "..", "scripts", "setup-jetson.sh"); + expect(fs.existsSync(scriptPath)).toBe(true); + const mode = fs.statSync(scriptPath).mode; + expect((mode & 0o111) !== 0).toBe(true); + }); + it("services no longer tell users to install brev-setup.sh", () => { const src = fs.readFileSync( path.join(import.meta.dirname, "..", "src", "lib", "services.ts"),