Skip to content

Instantly share code, notes, and snippets.

@HoKim98
Last active July 20, 2026 10:38
Show Gist options
  • Select an option

  • Save HoKim98/4a216d55bfd0130de6a4175bbbb86278 to your computer and use it in GitHub Desktop.

Select an option

Save HoKim98/4a216d55bfd0130de6a4175bbbb86278 to your computer and use it in GitHub Desktop.
Automatic Installation and Execution of Isaac-Sim on ANY environments (OS, containers, ...)
#!/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