Last active
July 20, 2026 10:38
-
-
Save HoKim98/4a216d55bfd0130de6a4175bbbb86278 to your computer and use it in GitHub Desktop.
Automatic Installation and Execution of Isaac-Sim on ANY environments (OS, containers, ...)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # An old shell can retain a deleted directory inode while a new shell resolves | |
| # the same textual path to its replacement. Re-enter the textual path on every | |
| # run so child processes never inherit the old inode. | |
| startup_cwd="${PWD:-}" | |
| if [[ -n "$startup_cwd" && -d "$startup_cwd" ]]; then | |
| cd -- "$startup_cwd" | |
| else | |
| cd -- "${HOME:?Current directory is invalid and HOME is not set}" | |
| fi | |
| unset startup_cwd | |
| SCRIPT_VERSION="0.3.4" | |
| DEFAULT_REPOSITORY="https://github.com/isaac-sim/IsaacSim.git" | |
| DEFAULT_IMAGE="nvcr.io/nvidia/isaac-sim" | |
| version="${AUTO_ISAAC_VERSION:-latest}" | |
| runtime="${AUTO_ISAAC_RUNTIME:-native}" | |
| install_base="${AUTO_ISAAC_HOME:-}" | |
| repository="${AUTO_ISAAC_REPO_URL:-$DEFAULT_REPOSITORY}" | |
| image_repository="${AUTO_ISAAC_IMAGE:-$DEFAULT_IMAGE}" | |
| update=0 | |
| rebuild=0 | |
| clean=0 | |
| launch=1 | |
| force_gui=0 | |
| declare -a kit_args=() | |
| function is_yes() { | |
| [[ "${1:-}" =~ ^([Yy]|[Yy][Ee][Ss]|1|true|TRUE)$ ]] | |
| } | |
| function fail() { | |
| echo "ERROR: $*" >&2 | |
| exit 1 | |
| } | |
| function usage() { | |
| cat <<'EOF' | |
| Usage: oneclick-isaac-sim.sh [options] [--] [Kit arguments...] | |
| Options: | |
| -V, --version VERSION | |
| -d, --install-dir PATH | |
| --runtime native|wsl|docker|podman|nerdctl|ctr | |
| --update | |
| --rebuild | |
| --clean | |
| --no-launch | |
| --force-unsupported-gui | |
| -h, --help | |
| Environment: | |
| AUTO_ISAAC_VERSION, AUTO_ISAAC_RUNTIME, AUTO_ISAAC_HOME | |
| AUTO_ISAAC_REPO_URL, AUTO_ISAAC_IMAGE | |
| AUTO_ISAAC_INSTALL_PACKAGES=Y, AUTO_ISAAC_FORCE_UNSUPPORTED_GUI=Y | |
| ACCEPT_EULA=Y, PRIVACY_CONSENT=Y | |
| EOF | |
| } | |
| function parse_arguments() { | |
| while (($#)); do | |
| case "$1" in | |
| -V|--version) (($# >= 2)) || fail "$1 requires a value"; version="$2"; shift 2 ;; | |
| --version=*) version="${1#*=}"; shift ;; | |
| -d|--install-dir) (($# >= 2)) || fail "$1 requires a value"; install_base="$2"; shift 2 ;; | |
| --install-dir=*) install_base="${1#*=}"; shift ;; | |
| --runtime) (($# >= 2)) || fail "$1 requires a value"; runtime="$2"; shift 2 ;; | |
| --runtime=*) runtime="${1#*=}"; shift ;; | |
| --update) update=1; shift ;; | |
| --rebuild) rebuild=1; shift ;; | |
| --clean) clean=1; rebuild=1; shift ;; | |
| --no-launch) launch=0; shift ;; | |
| --force-unsupported-gui) force_gui=1; shift ;; | |
| -h|--help) usage; exit 0 ;; | |
| --) shift; kit_args=("$@"); return ;; | |
| *) kit_args=("$@"); return ;; | |
| esac | |
| done | |
| } | |
| function command_exists() { | |
| command -v "$1" >/dev/null 2>&1 | |
| } | |
| function require_command() { | |
| command_exists "$1" || fail "Required command is unavailable: $1" | |
| } | |
| function detect_wsl() { | |
| in_wsl=0 | |
| if [[ -n "${WSL_INTEROP:-}" || -n "${WSL_DISTRO_NAME:-}" ]]; then | |
| in_wsl=1 | |
| fi | |
| } | |
| function ask() { | |
| local prompt="$1" answer | |
| if is_yes "${AUTO_ISAAC_INSTALL_PACKAGES:-}"; then | |
| return 0 | |
| fi | |
| [[ -r /dev/tty ]] || return 1 | |
| read -r -p "$prompt [y/N] " answer </dev/tty | |
| is_yes "$answer" | |
| } | |
| function install_linux_dependencies() { | |
| local -a missing=() packages=() prefix=() | |
| local pair command_name package_name | |
| local -a required=(git:git sha256sum:coreutils sed:sed sort:coreutils tail:coreutils tr:coreutils) | |
| if [[ "$runtime" == wsl || ("$runtime" == native && $in_wsl -eq 0) ]]; then | |
| required+=(make:build-essential python3:python3) | |
| fi | |
| [[ "$runtime" =~ ^(docker|podman|nerdctl|ctr)$ ]] && required+=("$runtime:$runtime") | |
| for pair in "${required[@]}"; do | |
| command_name="${pair%%:*}" | |
| package_name="${pair#*:}" | |
| command_exists "$command_name" || missing+=("$command_name") packages+=("$package_name") | |
| done | |
| if ((${#missing[@]} == 0)); then | |
| return 0 | |
| fi | |
| command_exists apt-get || fail "Missing commands: ${missing[*]}. Install the corresponding packages manually." | |
| if ((EUID != 0)); then | |
| command_exists sudo && sudo -n true >/dev/null 2>&1 || | |
| fail "Missing commands: ${missing[*]}. Passwordless sudo is unavailable; install them manually." | |
| prefix=(sudo -n) | |
| fi | |
| ask "Install required packages (${packages[*]})?" || fail "Package installation was not approved." | |
| "${prefix[@]}" apt-get update </dev/null | |
| "${prefix[@]}" apt-get install -y "${packages[@]}" </dev/null | |
| } | |
| function resolve_latest_version() { | |
| if [[ "$version" != latest ]]; then | |
| return 0 | |
| fi | |
| echo "Resolving latest stable Isaac Sim version from GitHub..." >&2 | |
| version="$(git ls-remote --refs --tags "$repository" 'v*' </dev/null | | |
| sed -nE 's#^.*refs/tags/v([0-9]+\.[0-9]+\.[0-9]+)$#\1#p' | | |
| sort -V | tail -n1)" | |
| [[ -n "$version" ]] || fail "No stable vMAJOR.MINOR.PATCH tag was found." | |
| echo "Latest stable Isaac Sim version: $version" >&2 | |
| } | |
| function resolve_windows_home() { | |
| local windows_home | |
| windows_home="$(cmd.exe /d /c 'echo %USERPROFILE%' </dev/null 2>/dev/null | tr -d '\r' | tail -n1)" | |
| [[ -n "$windows_home" && "$windows_home" != '%USERPROFILE%' ]] || | |
| fail "WSL could not obtain the Windows user profile. Set AUTO_ISAAC_HOME explicitly." | |
| wslpath -u "$windows_home" | |
| } | |
| function configure_paths() { | |
| if [[ -z "$install_base" ]]; then | |
| if ((in_wsl)) && [[ "$runtime" != wsl ]]; then | |
| install_base="$(resolve_windows_home)/.config/auto-isaac" | |
| else | |
| install_base="${HOME:?HOME is not set}/.config/auto-isaac" | |
| fi | |
| elif ((in_wsl)) && [[ "$runtime" != wsl ]] && [[ "$install_base" =~ ^[A-Za-z]:[\\/] ]]; then | |
| install_base="$(wslpath -u "$install_base")" | |
| fi | |
| version_root="$install_base/$version" | |
| repo_dir="$version_root/repo" | |
| state_dir="$version_root/state" | |
| cache_dir="$version_root/cache" | |
| mkdir -p "$state_dir" "$cache_dir" | |
| echo "Installation directory: $version_root" >&2 | |
| } | |
| function kit_is_headless() { | |
| local arg | |
| for arg in "${kit_args[@]}"; do [[ "$arg" == --no-window ]] && return 0; done | |
| return 1 | |
| } | |
| function guard_gui() { | |
| local reason="" | |
| if ((launch == 0)) || kit_is_headless || | |
| is_yes "${AUTO_ISAAC_FORCE_UNSUPPORTED_GUI:-}" || ((force_gui)); then | |
| return 0 | |
| fi | |
| if ((in_wsl)) && [[ "$runtime" != native ]]; then | |
| reason="runtime '$runtime' cannot provide a verified GUI from WSL" | |
| elif ((in_wsl == 0)) && [[ "$runtime" == native ]] && | |
| [[ -z "${DISPLAY:-}" && -z "${WAYLAND_DISPLAY:-}" ]]; then | |
| reason="no Linux graphical session was detected (DISPLAY and WAYLAND_DISPLAY are unset)" | |
| fi | |
| [[ -z "$reason" ]] && return 0 | |
| echo "WARNING: Isaac Sim GUI is unavailable: $reason." >&2 | |
| local answer | |
| [[ -r /dev/tty ]] || fail "Use --no-window or AUTO_ISAAC_FORCE_UNSUPPORTED_GUI=Y." | |
| read -r -p "Continue anyway? [y/N] " answer </dev/tty | |
| is_yes "$answer" || fail "Execution cancelled." | |
| } | |
| function accept_eula() { | |
| [[ -f "$state_dir/eula.accepted" ]] && { export ACCEPT_EULA=Y; return; } | |
| if ! is_yes "${ACCEPT_EULA:-}"; then | |
| local answer | |
| [[ -r /dev/tty ]] || fail "Set ACCEPT_EULA=Y for non-interactive execution." | |
| read -r -p "Accept the NVIDIA Isaac Sim license? [y/N] " answer </dev/tty | |
| is_yes "$answer" || fail "The license was not accepted." | |
| fi | |
| export ACCEPT_EULA=Y | |
| touch "$state_dir/eula.accepted" | |
| } | |
| function download_source() { | |
| local tag="v$version" partial="$version_root/repo.partial" actual | |
| if [[ -d "$repo_dir/.git" ]]; then | |
| echo "Isaac Sim $version source already downloaded." | |
| ((update)) && git -C "$repo_dir" fetch --depth 1 origin "refs/tags/$tag:refs/tags/$tag" </dev/null | |
| else | |
| rm -rf -- "$partial" | |
| echo "Downloading Isaac Sim $tag..." | |
| git clone --depth 1 --branch "$tag" "$repository" "$partial" </dev/null | |
| mv -- "$partial" "$repo_dir" | |
| fi | |
| actual="$(git -C "$repo_dir" describe --tags --exact-match </dev/null 2>/dev/null || true)" | |
| [[ "$actual" == "$tag" ]] || fail "Downloaded source is $actual, not $tag." | |
| } | |
| function script_digest() { | |
| printf '%s\n' "$SCRIPT_VERSION" | sha256sum | awk '{print $1}' | |
| } | |
| function build_digest() { | |
| { script_digest; git -C "$repo_dir" rev-parse HEAD </dev/null; printf '%s\n' "$runtime" "${toolchain_identity:-default}"; } | | |
| sha256sum | awk '{print $1}' | |
| } | |
| function install_required_linux_compiler() { | |
| local major="$1" | |
| local -a prefix=() | |
| command_exists apt-get || | |
| fail "Isaac Sim requires GCC $major, but this system has no gcc-$major and automatic installation currently requires apt-get." | |
| if ((EUID != 0)); then | |
| command_exists sudo && sudo -n true >/dev/null 2>&1 || | |
| fail "Isaac Sim requires GCC $major. Install gcc-$major and g++-$major manually; passwordless sudo is unavailable." | |
| prefix=(sudo -n) | |
| fi | |
| ask "Install the GCC $major toolchain required by this Isaac Sim version?" || | |
| fail "GCC $major installation was not approved." | |
| "${prefix[@]}" apt-get update </dev/null | |
| "${prefix[@]}" apt-get install -y "gcc-$major" "g++-$major" </dev/null || | |
| fail "The package manager could not install gcc-$major and g++-$major. Enable the repository that provides this toolchain or install it manually." | |
| } | |
| function resolve_linux_toolchain() { | |
| local requirement major system_major | |
| requirement="$(sed -nE '/platform:linux-x86_64.*version_check_gcc_version/ { | |
| s/^.*= *"([0-9]+)(\.[^"]*)?".*$/\1/p | |
| q | |
| }' "$repo_dir/repo.toml")" | |
| [[ -n "$requirement" ]] || fail "Could not determine the required GCC version from $repo_dir/repo.toml" | |
| major="$requirement" | |
| if command_exists gcc && command_exists g++; then | |
| system_major="$(gcc -dumpfullversion -dumpversion | sed 's/\..*$//')" | |
| if [[ "$system_major" == "$major" ]]; then | |
| linux_cc="$(command -v gcc)" | |
| linux_cxx="$(command -v g++)" | |
| fi | |
| fi | |
| if [[ -z "${linux_cc:-}" ]]; then | |
| if ! command_exists "gcc-$major" || ! command_exists "g++-$major"; then | |
| install_required_linux_compiler "$major" | |
| fi | |
| linux_cc="$(command -v "gcc-$major")" | |
| linux_cxx="$(command -v "g++-$major")" | |
| fi | |
| toolchain_identity="$($linux_cc -dumpfullversion -dumpversion)@$linux_cc" | |
| echo "Using Isaac Sim required compiler: GCC $toolchain_identity" >&2 | |
| } | |
| function install_windows_build_tools() { | |
| require_command winget.exe | |
| ask "Install Visual Studio 2022 Build Tools with winget?" || fail "Build Tools installation was not approved." | |
| winget.exe install --id Microsoft.VisualStudio.2022.BuildTools --exact </dev/null \ | |
| --accept-package-agreements --accept-source-agreements --silent \ | |
| --override '--wait --quiet --norestart --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended' || true | |
| } | |
| function run_windows_build() { | |
| local build_win log status=0 | |
| build_win="$(wslpath -w "$repo_dir/build.bat")" | |
| log="$state_dir/windows-build.log" | |
| echo "Running Windows incremental Release build..." | |
| cmd.exe /d /c call "$build_win" -r </dev/null 2>&1 | tee "$log" || status=${PIPESTATUS[0]} | |
| if ((status != 0)) && grep -Eqi 'visual studio|msvc|compiler.*(missing|not found)|build tools' "$log"; then | |
| install_windows_build_tools | |
| status=0 | |
| cmd.exe /d /c call "$build_win" -r </dev/null 2>&1 | tee "$log" || status=${PIPESTATUS[0]} | |
| fi | |
| ((status == 0)) || fail "Windows build failed with exit code $status. See $log" | |
| } | |
| function build_native() { | |
| local target kit state current cc_cmd cxx_cmd toolchain_dir build_input=/dev/null | |
| local -a build_args=(-r) | |
| [[ -r /dev/tty ]] && build_input=/dev/tty | |
| if ((in_wsl)) && [[ "$runtime" == native ]]; then | |
| target=windows-x86_64 | |
| kit="$repo_dir/_build/$target/release/kit/kit.exe" | |
| else | |
| target=linux-x86_64 | |
| kit="$repo_dir/_build/$target/release/kit/kit" | |
| resolve_linux_toolchain | |
| fi | |
| state="$state_dir/$runtime-build.sha256" | |
| current="$(build_digest)" | |
| if [[ -f "$kit" && -f "$state" && "$(<"$state")" == "$current" ]] && ((rebuild == 0)); then | |
| echo "Build inputs unchanged; skipping compilation." | |
| return | |
| fi | |
| accept_eula | |
| # Isaac Sim's source build checks this repository-local marker instead of | |
| # consistently honoring ACCEPT_EULA across every build entry point. | |
| touch "$repo_dir/.eula_accepted" | |
| if [[ "$target" == windows-x86_64 ]]; then | |
| run_windows_build | |
| else | |
| cc_cmd="${CC:-$linux_cc}" | |
| cxx_cmd="${CXX:-$linux_cxx}" | |
| toolchain_dir="$state_dir/toolchain/bin" | |
| mkdir -p "$toolchain_dir" | |
| ln -sfn "$cc_cmd" "$toolchain_dir/gcc" | |
| ln -sfn "$cxx_cmd" "$toolchain_dir/g++" | |
| ln -sfn "$cc_cmd" "$toolchain_dir/cc" | |
| ln -sfn "$cxx_cmd" "$toolchain_dir/c++" | |
| ((clean)) && build_args=(-x -r) | |
| echo "Running Linux incremental Release build..." | |
| (cd "$repo_dir" && PATH="$toolchain_dir:$PATH" CC="$cc_cmd" CXX="$cxx_cmd" \ | |
| ./build.sh "${build_args[@]}" <"$build_input") | |
| fi | |
| [[ -f "$kit" ]] || fail "Build completed without Kit: $kit" | |
| build_digest > "$state" | |
| } | |
| function launch_native() { | |
| local build_dir kit app launch_input=/dev/null | |
| local -a root_args=() | |
| [[ -r /dev/tty ]] && launch_input=/dev/tty | |
| if ((in_wsl)) && [[ "$runtime" == native ]]; then | |
| build_dir="$repo_dir/_build/windows-x86_64/release" | |
| kit="$build_dir/kit/kit.exe" | |
| app="$build_dir/apps/isaacsim.exp.full.kit" | |
| echo "Launching Windows Isaac Sim..." | |
| cd "$build_dir" | |
| exec "$kit" "$(wslpath -w "$app")" "${kit_args[@]}" <"$launch_input" | |
| fi | |
| build_dir="$repo_dir/_build/linux-x86_64/release" | |
| ((EUID == 0)) && root_args+=(--allow-root) | |
| exec "$build_dir/kit/kit" "$build_dir/apps/isaacsim.exp.full.kit" "${root_args[@]}" "${kit_args[@]}" <"$launch_input" | |
| } | |
| function run_container() { | |
| local image="$image_repository:$version" | |
| accept_eula | |
| if ((update)) || ! "$runtime" image inspect "$image" >/dev/null 2>&1; then | |
| "$runtime" pull "$image" | |
| else | |
| echo "Container image already present: $image" | |
| fi | |
| if ((launch == 0)); then | |
| return 0 | |
| fi | |
| mkdir -p "$cache_dir" | |
| case "$runtime" in | |
| docker|nerdctl) | |
| exec "$runtime" run --rm --gpus all --network host -e ACCEPT_EULA=Y \ | |
| -e "PRIVACY_CONSENT=${PRIVACY_CONSENT:-N}" --entrypoint /isaac-sim/kit/kit \ | |
| "$image" /isaac-sim/apps/isaacsim.exp.full.kit "${kit_args[@]}" ;; | |
| podman) | |
| exec podman run --rm --device nvidia.com/gpu=all --network host -e ACCEPT_EULA=Y \ | |
| -e "PRIVACY_CONSENT=${PRIVACY_CONSENT:-N}" --entrypoint /isaac-sim/kit/kit \ | |
| "$image" /isaac-sim/apps/isaacsim.exp.full.kit "${kit_args[@]}" ;; | |
| ctr) | |
| exec ctr run --rm --gpus 0 "$image" "auto-isaac-$$" ./runheadless.sh "${kit_args[@]}" ;; | |
| esac | |
| } | |
| parse_arguments "$@" | |
| detect_wsl | |
| case "$runtime" in native|wsl|docker|podman|nerdctl|ctr) ;; *) fail "Unsupported runtime: $runtime" ;; esac | |
| [[ "$runtime" != wsl || $in_wsl == 1 ]] || fail "--runtime wsl requires WSL." | |
| install_linux_dependencies | |
| resolve_latest_version | |
| version="${version#v}" | |
| configure_paths | |
| guard_gui | |
| if [[ "$runtime" =~ ^(docker|podman|nerdctl|ctr)$ ]]; then | |
| run_container | |
| else | |
| download_source | |
| build_native | |
| if ((launch)); then | |
| launch_native | |
| fi | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment