Files
OpenShell/deploy/docker/devshell-bootstrap.sh
T

304 lines
9.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# Bootstrap an OpenShell development checkout inside a sandbox image.
set -euo pipefail
REPO_SLUG="${OPENSHELL_DEVSHELL_REPO_SLUG:-NVIDIA/OpenShell}"
export HOME=/sandbox
export USER=sandbox
export MISE_CACHE_DIR=/sandbox/.cache/mise
export CARGO_HOME=/sandbox/.cargo
path_prepend() {
case ":${PATH}:" in
*":$1:"*) ;;
*) PATH="$1:${PATH}" ;;
esac
}
ld_prepend() {
case ":${LD_LIBRARY_PATH:-}:" in
*":$1:"*) ;;
*) LD_LIBRARY_PATH="$1:${LD_LIBRARY_PATH:-}" ;;
esac
}
preload_prepend() {
case ":${LD_PRELOAD:-}:" in
*":$1:"*) ;;
*) LD_PRELOAD="$1${LD_PRELOAD:+:${LD_PRELOAD}}" ;;
esac
}
find_libclang_dir() {
local dir
for dir in \
/sandbox/.deps/usr/lib/llvm-*/lib \
/usr/lib/llvm-*/lib \
/sandbox/.deps/usr/lib/*-linux-gnu \
/usr/lib/*-linux-gnu \
/usr/local/lib \
/usr/lib64; do
[ -d "$dir" ] || continue
if [ -e "$dir/libclang.so" ] \
|| compgen -G "$dir/libclang-*.so" >/dev/null \
|| compgen -G "$dir/libclang.so.*" >/dev/null; then
printf '%s\n' "$dir"
return 0
fi
done
return 1
}
find_clang_resource_include() {
local dir
for dir in \
/sandbox/.deps/usr/lib/llvm-*/lib/clang/*/include \
/usr/lib/llvm-*/lib/clang/*/include; do
if [ -f "$dir/stddef.h" ]; then
printf '%s\n' "$dir"
return 0
fi
done
return 1
}
find_z3_header() {
local header
for header in /sandbox/.deps/usr/include/z3.h /usr/local/include/z3.h /usr/include/z3.h; do
if [ -f "$header" ]; then
printf '%s\n' "$header"
return 0
fi
done
return 1
}
find_z3_libdir() {
local dir
for dir in /sandbox/.deps/usr/lib/*-linux-gnu /usr/local/lib /usr/lib/*-linux-gnu /usr/lib64; do
[ -d "$dir" ] || continue
if [ -e "$dir/libz3.so" ] || [ -e "$dir/libz3.a" ]; then
printf '%s\n' "$dir"
return 0
fi
done
return 1
}
find_nss_wrapper() {
local lib
for lib in \
/sandbox/.deps/usr/lib/*-linux-gnu/libnss_wrapper.so \
/usr/lib/*-linux-gnu/libnss_wrapper.so \
/usr/lib64/libnss_wrapper.so; do
if [ -f "$lib" ]; then
printf '%s\n' "$lib"
return 0
fi
done
return 1
}
etc_hosts_has_localhost() {
grep -E '^(127\.0\.0\.1|::1)[[:space:]].*localhost' /etc/hosts >/dev/null 2>&1
}
native_deps_ready() {
find_libclang_dir >/dev/null \
&& find_clang_resource_include >/dev/null \
&& find_z3_header >/dev/null \
&& find_z3_libdir >/dev/null \
&& { etc_hosts_has_localhost || find_nss_wrapper >/dev/null; }
}
ensure_native_deps() {
native_deps_ready && return 0
command -v apt-get >/dev/null 2>&1 || return 0
command -v dpkg-deb >/dev/null 2>&1 || return 0
[ -f /etc/apt/sources.list.d/ubuntu.sources ] || return 0
local apt_root=/sandbox/.apt
mkdir -p \
"$apt_root/apt.conf.d" \
"$apt_root/sources" \
"$apt_root/lists/partial" \
"$apt_root/cache/archives/partial" \
/sandbox/.deps
cp /etc/apt/sources.list.d/ubuntu.sources "$apt_root/sources/ubuntu.sources"
local apt_opts=(
-o Dir::Etc::main=/dev/null
-o "Dir::Etc::parts=$apt_root/apt.conf.d"
-o Dir::Etc::sourcelist=/dev/null
-o "Dir::Etc::sourceparts=$apt_root/sources"
-o "Dir::State::Lists=$apt_root/lists"
-o Dir::State::status=/var/lib/dpkg/status
-o "Dir::Cache=$apt_root/cache"
-o "Dir::Cache::archives=$apt_root/cache/archives"
-o Debug::NoLocking=1
-o Acquire::Languages=none
)
if ! compgen -G "$apt_root/lists/*_Packages*" >/dev/null; then
apt-get "${apt_opts[@]}" update
fi
if ! compgen -G "$apt_root/cache/archives/libclang-*.deb" >/dev/null \
|| ! compgen -G "$apt_root/cache/archives/libz3-dev_*.deb" >/dev/null; then
apt-get "${apt_opts[@]}" --download-only --reinstall -y install \
libclang-dev \
libz3-dev \
libnss-wrapper \
pkg-config
fi
local deb
for deb in "$apt_root"/cache/archives/*.deb; do
[ -f "$deb" ] || continue
dpkg-deb -x "$deb" /sandbox/.deps
done
}
configure_native_deps_env() {
local libclang_dir clang_include z3_header z3_libdir nss_wrapper
if [ -d /sandbox/.deps/usr/bin ]; then
path_prepend /sandbox/.deps/usr/bin
fi
if libclang_dir="$(find_libclang_dir)"; then
export LIBCLANG_PATH="$libclang_dir"
ld_prepend "$libclang_dir"
fi
if clang_include="$(find_clang_resource_include)"; then
export BINDGEN_EXTRA_CLANG_ARGS="-I${clang_include}${BINDGEN_EXTRA_CLANG_ARGS:+ ${BINDGEN_EXTRA_CLANG_ARGS}}"
fi
if z3_header="$(find_z3_header)"; then
export Z3_SYS_Z3_HEADER="$z3_header"
fi
if z3_libdir="$(find_z3_libdir)"; then
export Z3_LIBRARY_PATH_OVERRIDE="$z3_libdir"
ld_prepend "$z3_libdir"
fi
if [ -d /sandbox/.deps/usr/lib/aarch64-linux-gnu/pkgconfig ]; then
export PKG_CONFIG_PATH="/sandbox/.deps/usr/lib/aarch64-linux-gnu/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}"
export PKG_CONFIG_SYSROOT_DIR=/sandbox/.deps
fi
if ! etc_hosts_has_localhost && nss_wrapper="$(find_nss_wrapper)"; then
{
printf '127.0.0.1 localhost localhost.localdomain\n'
printf '::1 localhost ip6-localhost ip6-loopback\n'
printf '8.8.8.8 dns.google\n'
grep -v -E '^(127\.0\.0\.1|::1)[[:space:]]' /etc/hosts || true
} > /sandbox/.nss-hosts
cat /etc/passwd > /sandbox/.nss-passwd
if ! grep -q -E "^$(id -un):" /sandbox/.nss-passwd; then
printf '%s:x:%s:%s::%s:%s\n' \
"$(id -un)" "$(id -u)" "$(id -g)" "$HOME" "${SHELL:-/bin/bash}" \
>> /sandbox/.nss-passwd
fi
cat /etc/group > /sandbox/.nss-group
if ! grep -q -E "^$(id -gn):" /sandbox/.nss-group; then
printf '%s:x:%s:%s\n' "$(id -gn)" "$(id -g)" "$(id -un)" \
>> /sandbox/.nss-group
fi
export NSS_WRAPPER_HOSTS=/sandbox/.nss-hosts
export NSS_WRAPPER_PASSWD=/sandbox/.nss-passwd
export NSS_WRAPPER_GROUP=/sandbox/.nss-group
export RUST_TEST_THREADS="${RUST_TEST_THREADS:-1}"
preload_prepend "$nss_wrapper"
fi
export PATH LD_LIBRARY_PATH LD_PRELOAD
}
write_shell_profile() {
{
printf 'export HOME=%q\n' "$HOME"
printf 'export USER=%q\n' "$USER"
printf 'export MISE_CACHE_DIR=%q\n' "$MISE_CACHE_DIR"
printf 'export CARGO_HOME=%q\n' "$CARGO_HOME"
printf 'export MISE_DATA_DIR=%q\n' "$MISE_DATA_DIR"
printf 'export RUSTUP_HOME=%q\n' "$RUSTUP_HOME"
printf 'export PATH=%q\n' "$PATH"
[ -z "${LD_LIBRARY_PATH:-}" ] || printf 'export LD_LIBRARY_PATH=%q\n' "$LD_LIBRARY_PATH"
[ -z "${LD_PRELOAD:-}" ] || printf 'export LD_PRELOAD=%q\n' "$LD_PRELOAD"
[ -z "${LIBCLANG_PATH:-}" ] || printf 'export LIBCLANG_PATH=%q\n' "$LIBCLANG_PATH"
[ -z "${BINDGEN_EXTRA_CLANG_ARGS:-}" ] || printf 'export BINDGEN_EXTRA_CLANG_ARGS=%q\n' "$BINDGEN_EXTRA_CLANG_ARGS"
[ -z "${PKG_CONFIG_PATH:-}" ] || printf 'export PKG_CONFIG_PATH=%q\n' "$PKG_CONFIG_PATH"
[ -z "${PKG_CONFIG_SYSROOT_DIR:-}" ] || printf 'export PKG_CONFIG_SYSROOT_DIR=%q\n' "$PKG_CONFIG_SYSROOT_DIR"
[ -z "${Z3_SYS_Z3_HEADER:-}" ] || printf 'export Z3_SYS_Z3_HEADER=%q\n' "$Z3_SYS_Z3_HEADER"
[ -z "${Z3_LIBRARY_PATH_OVERRIDE:-}" ] || printf 'export Z3_LIBRARY_PATH_OVERRIDE=%q\n' "$Z3_LIBRARY_PATH_OVERRIDE"
[ -z "${NSS_WRAPPER_HOSTS:-}" ] || printf 'export NSS_WRAPPER_HOSTS=%q\n' "$NSS_WRAPPER_HOSTS"
[ -z "${NSS_WRAPPER_PASSWD:-}" ] || printf 'export NSS_WRAPPER_PASSWD=%q\n' "$NSS_WRAPPER_PASSWD"
[ -z "${NSS_WRAPPER_GROUP:-}" ] || printf 'export NSS_WRAPPER_GROUP=%q\n' "$NSS_WRAPPER_GROUP"
[ -z "${RUST_TEST_THREADS:-}" ] || printf 'export RUST_TEST_THREADS=%q\n' "$RUST_TEST_THREADS"
} > /sandbox/.profile
cp /sandbox/.profile /sandbox/.bash_profile
printf 'source /sandbox/.profile\n' > /sandbox/.bashrc
}
configure_mise() {
if command -v mise >/dev/null 2>&1 && [ -d /usr/local/share/openshell/mise ]; then
export MISE_DATA_DIR=/usr/local/share/openshell/mise
export RUSTUP_HOME=/usr/local/share/openshell/mise/rustup
path_prepend /usr/local/share/openshell/mise/cargo/bin
path_prepend /usr/local/bin
path_prepend /usr/local/share/openshell/mise/shims
return
fi
if command -v mise >/dev/null 2>&1 && [ -d /opt/mise ]; then
export MISE_DATA_DIR=/opt/mise
export RUSTUP_HOME=/opt/mise/rustup
path_prepend /opt/mise/cargo/bin
path_prepend /usr/local/bin
path_prepend /opt/mise/shims
return
fi
export MISE_DATA_DIR=/sandbox/.local/share/mise
export RUSTUP_HOME=/sandbox/.rustup
path_prepend /sandbox/.cargo/bin
path_prepend /sandbox/.local/bin
path_prepend /sandbox/.local/share/mise/shims
if ! command -v mise >/dev/null 2>&1; then
mkdir -p /sandbox/.local/bin
curl -fsSL https://mise.run | sh
fi
}
clone_repo() {
if [ -d /sandbox/openshell/.git ]; then
return
fi
if command -v gh >/dev/null 2>&1; then
gh repo clone "$REPO_SLUG" /sandbox/openshell \
|| git clone "https://github.com/${REPO_SLUG}.git" /sandbox/openshell
else
git clone "https://github.com/${REPO_SLUG}.git" /sandbox/openshell
fi
}
mkdir -p /sandbox /sandbox/.cargo /sandbox/.cache/mise
configure_mise
ensure_native_deps
configure_native_deps_env
write_shell_profile
clone_repo
cd /sandbox/openshell
mise trust >/dev/null
MISE_YES=1 mise install
if [ -f pyproject.toml ] && ! .venv/bin/python -c 'import grpc_tools.protoc' >/dev/null 2>&1; then
uv sync --locked
fi
exec "$@"