From cd1322cdf20f0de1b54748e0b908d0bdf5f6c713 Mon Sep 17 00:00:00 2001 From: Diego Volpatto Date: Mon, 17 Aug 2026 22:54:24 -0300 Subject: [PATCH] Enhance provisioning scripts to include Pixi installation - Updated README.md to reflect the addition of Pixi installation across Ubuntu, macOS, and Windows provisioning scripts. - Modified Ubuntu provisioning script to install Pixi for the invoking user, with CLI flags to control installation. - Enhanced macOS provisioning script to include Pixi installation via Homebrew, with appropriate flags. - Updated Windows PowerShell script to install Pixi for the current user, with verification of installation. - Adjusted CUDA and NVIDIA driver installation logic to clarify independence and requirements. --- README.md | 107 ++++++++++++++----- scripts/provision-macos.sh | 68 +++++++++--- scripts/provision-ubuntu.sh | 207 +++++++++++++++++++++++++++++++----- scripts/provision-win.ps1 | 97 ++++++++++++++--- 4 files changed, 404 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 895261b..c46411a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # provisioning -Provisioning scripts for developer machines on Ubuntu, macOS, and Windows. They install common tooling (Git, VS Code, Docker), set up a user-scoped Miniconda with conda-forge, and optionally prepare CUDA. +Provisioning scripts for developer machines on Ubuntu, macOS, and Windows. They install common tooling (Git, VS Code, Docker), Pixi, a user-scoped Conda distribution, and optionally prepare NVIDIA drivers and CUDA. These scripts are CLI-configurable and expose small profiles to make running them predictable and repeatable. @@ -20,9 +20,11 @@ Ubuntu CLI flags | `--install-vscode` | `true` | Install Microsoft VS Code repo + `code` package | | `--install-docker` | `true` | Install Docker Engine from Docker apt repo | | `--install-cuda` | `false` | Install NVIDIA CUDA toolkit package | -| `--install-driver` | `false` | Install NVIDIA GPU driver (bare metal only; ignored on WSL) | -| `--cuda-toolkit-pkg` | `cuda-toolkit-12-4` | Specific CUDA toolkit package name to install | +| `--install-driver` | `false` | Install NVIDIA GPU driver on bare-metal Ubuntu; skipped on WSL | +| `--cuda-toolkit-pkg` | `cuda-toolkit` | Toolkit package from the NVIDIA repository; may be version-pinned | +| `--nvidia-driver-pkg` | `nvidia-open` | NVIDIA driver package used on bare-metal Ubuntu | | `--install-python` | `true` | Install system Python3 + venv + pip | +| `--install-pixi` | `true` | Install Pixi for the invoking user | macOS CLI flags @@ -37,6 +39,7 @@ macOS CLI flags | `--miniforge-prefix` | `$HOME/miniforge3` | Install path for Miniforge | | `--install-llvm` | `true` | Install Homebrew llvm and libomp | | `--install-python` | `true` | Install Homebrew Python (includes venv) | +| `--install-pixi` | `true` | Install Pixi through Homebrew | Windows PowerShell parameters @@ -47,6 +50,7 @@ Windows PowerShell parameters | `-InstallDocker` | `$true` | Install Docker Desktop via Chocolatey | | `-InstallWindowsTerminal` | `$true` | Install Windows Terminal | | `-InstallCmder` | `$true` | Install Cmder (or CmderMini via `-CmderPackageId`) | +| `-InstallPixi` | `$true` | Install Pixi for the current user | | `-InstallCUDA` | `$false` | Install CUDA toolkit via Chocolatey | | `-InstallNvidiaDriver` | `$false` | Install NVIDIA display driver via Chocolatey | | `-Version` | `$null` | Optional version pin (strings) for many packages (e.g., `-CudaToolkitVersion`) | @@ -54,9 +58,27 @@ Windows PowerShell parameters ## What they install (summary) -- Ubuntu: base dev tools (git, git-lfs, build-essential, cmake, ninja, unzip/xz/7zip), optional VS Code, optional Docker Engine, Miniconda for the invoking user (conda-forge configured), optional CUDA toolkit + driver. -- macOS: Homebrew, core dev tooling via Homebrew (git, git-lfs, cmake, ninja, pkg-config, optional llvm/libomp), common casks (VS Code, iTerm2, Docker Desktop), Miniforge (conda-forge) installed into the user's home. -- Windows: core tools via Chocolatey (git, 7zip, cmake, ninja, Visual Studio Build Tools), optional VS Code, Windows Terminal, Cmder, Docker Desktop, Miniconda, optional CUDA toolkit + driver. +- Ubuntu: base dev tools, Pixi, optional VS Code and Docker Engine, Miniconda for the invoking user, and independently selectable NVIDIA driver and CUDA Toolkit. +- macOS: Homebrew, Pixi, core dev tooling, common casks, and Miniforge installed into the user's home. +- Windows: core tools via Chocolatey, Pixi, optional desktop tools, Miniconda, and independently selectable NVIDIA driver and CUDA Toolkit. + +## PyTorch and torch-flash recommendation + +Prebuilt PyTorch, Conda, and Pixi packages normally provide their own CUDA user-space runtime. For a PyTorch workstation, install a current NVIDIA host driver and omit the system CUDA Toolkit unless `nvcc` or another Toolkit development tool is explicitly required. + +After provisioning Linux or Windows, the `torch-flash` GPU environment can be created from its repository with: + +```bash +pixi install -e gpu +pixi run -e gpu python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())" +``` + +On Apple Silicon, use the normal environment and PyTorch MPS instead: + +```bash +pixi install +pixi run python -c "import torch; print(torch.backends.mps.is_available())" +``` ## Ubuntu: usage, profiles and CLI flags @@ -65,14 +87,14 @@ Run from the repository root. The script requires root to install system package Basic run: ```bash -sudo -E bash scripts/provision-ubuntu.sh +sudo bash scripts/provision-ubuntu.sh ``` Profiles -- `--profile=default` (default) -> VS Code ON, Docker ON -- `--profile=minimal` -> VS Code ON, Docker OFF -- `--profile=gpu` -> VS Code ON, Docker ON, CUDA toolkit ON (driver optional) +- `--profile=default` (default) -> VS Code ON, Docker ON, Pixi ON +- `--profile=minimal` -> VS Code ON, Docker OFF, Pixi ON +- `--profile=gpu` -> VS Code ON, Docker ON, Pixi ON, NVIDIA driver ON, CUDA Toolkit ON CLI flags (override profile defaults) @@ -80,40 +102,53 @@ CLI flags (override profile defaults) - `--install-docker=true|false` - `--install-cuda=true|false` - `--install-driver=true|false` -- `--cuda-toolkit-pkg=cuda-toolkit-12-4` (or `cuda-toolkit`) +- `--cuda-toolkit-pkg=cuda-toolkit` (or an available version such as `cuda-toolkit-13-3`) +- `--nvidia-driver-pkg=nvidia-open` (use `cuda-drivers` for proprietary kernel modules) - `--install-python=true|false` +- `--install-pixi=true|false` Examples - Default profile (VS Code + Docker): ```bash -sudo -E bash scripts/provision-ubuntu.sh +sudo bash scripts/provision-ubuntu.sh ``` - Minimal profile (skip Docker): ```bash -sudo -E bash scripts/provision-ubuntu.sh --profile=minimal +sudo bash scripts/provision-ubuntu.sh --profile=minimal ``` - GPU profile but explicitly disable Docker: ```bash -sudo -E bash scripts/provision-ubuntu.sh --profile=gpu --install-docker=false +sudo bash scripts/provision-ubuntu.sh --profile=gpu --install-docker=false ``` - + +- PyTorch GPU workstation: install the driver but leave CUDA runtime selection to Pixi: + +```bash +sudo bash scripts/provision-ubuntu.sh \ + --profile=gpu \ + --install-docker=false \ + --install-cuda=false +``` + - Strict verification (optional): ```bash -VERIFY_STRICT=true sudo -E bash scripts/provision-ubuntu.sh --profile=minimal +sudo env VERIFY_STRICT=true bash scripts/provision-ubuntu.sh --profile=minimal ``` What the script does (high level) - Detects the real (non-root) user who invoked sudo so Miniconda installs into their home. -- Adds/normalizes vendor apt repositories (Docker, Microsoft Code) in an idempotent way; CUDA uses distro packages. +- Adds/normalizes vendor apt repositories for Docker, Microsoft Code, and NVIDIA. - Installs base packages and tooling via apt. +- Installs Pixi for the invoking non-root user. +- Optionally installs the NVIDIA driver and CUDA Toolkit as independent choices. - Installs Miniconda into the invoking user's home and runs `conda init` for bash and zsh. Notes & gotchas @@ -122,7 +157,9 @@ Notes & gotchas - The script bundles logic to detect and resolve conflicting VS Code apt repo entries before running `apt update`. - Miniconda installer auto-selects x86_64/arm64. - Docker: after install, log out/in or run `newgrp docker` to use Docker without sudo. On WSL, the script skips engine install and recommends Docker Desktop. -- CUDA: driver install is for bare metal Ubuntu; on WSL2 use Windows driver. Reboot recommended after driver install. +- NVIDIA packages: the script derives the repository from the Ubuntu release and architecture. A pinned Toolkit package must exist in that repository; the unversioned `cuda-toolkit` default tracks the latest supported release. +- CUDA driver: driver installation is for bare-metal Ubuntu only. On WSL2, use the Windows NVIDIA driver. Reboot after installing or changing the bare-metal driver. +- Pixi: installed under `~/.pixi`; open a new terminal if it is not immediately on `PATH`. ## macOS: usage, profiles and CLI flags @@ -136,9 +173,9 @@ bash scripts/provision-macos.sh Profiles -- `--profile=default` (default) -> VS Code ON, Docker ON -- `--profile=minimal` -> VS Code ON, Docker OFF -- `--profile=gpu` -> VS Code ON, Docker ON (CUDA unsupported on macOS; kept for parity) +- `--profile=default` (default) -> VS Code ON, Docker ON, Pixi ON +- `--profile=minimal` -> VS Code ON, Docker OFF, Pixi ON +- `--profile=gpu` -> VS Code ON, Docker ON, Pixi ON (CUDA unsupported on macOS; kept for parity) CLI flags (override profile defaults) @@ -150,6 +187,7 @@ CLI flags (override profile defaults) - `--miniforge-prefix=/path/to/miniforge3` # default: $HOME/miniforge3 - `--install-llvm=true|false` - `--install-python=true|false` +- `--install-pixi=true|false` What the script does (high level) @@ -157,11 +195,13 @@ What the script does (high level) - Installs dev tools via Homebrew (git, git-lfs, cmake, ninja, pkg-config, optional llvm/libomp, optional Python). - Installs casks: VS Code, iTerm2, Docker Desktop (if enabled). - Installs Miniforge into `--miniforge-prefix`, sets conda-forge (strict), runs `conda init` for bash and zsh. +- Installs Pixi through Homebrew. - Optionally installs Rosetta 2 on Apple Silicon when requested. Notes & gotchas - Run as a normal user; Homebrew prefers user context. +- Do not use `sudo` to launch the script; it now exits early with a clear error because Homebrew does not support root execution. - After installing Docker Desktop, launch the app once to finish setup and grant permissions. - If Rosetta is enabled, you may be prompted for your password. - Open a NEW terminal to get `conda` on PATH, or run `source "$MINIFORGE_PREFIX/etc/profile.d/conda.sh"` then `conda activate`. @@ -182,9 +222,9 @@ Set-ExecutionPolicy Bypass -Scope Process -Force Profiles (use `-Profile `) and parameters -- `-Profile default` -> default toggles as declared in the script -- `-Profile minimal` -> skips Docker by default -- `-Profile gpu` -> enables CUDA + NVIDIA driver by default +- `-Profile default` -> default toggles as declared in the script, including Pixi +- `-Profile minimal` -> skips Docker but keeps Pixi enabled +- `-Profile gpu` -> enables CUDA + NVIDIA driver and keeps Pixi enabled Parameters (examples) @@ -192,6 +232,7 @@ Parameters (examples) - `-InstallDocker:$true/$false` - `-InstallWindowsTerminal:$true/$false` - `-InstallCmder:$true/$false` +- `-InstallPixi:$true/$false` - `-InstallCUDA:$true/$false` - `-InstallNvidiaDriver:$true/$false` - Version pins (strings): `-CudaToolkitVersion`, `-NvidiaDriverVersion`, `-GitVersion`, `-VSCodeVersion`, `-CMakeVersion`, `-NinjaVersion`, `-VSBuildToolsVersion`, `-MinicondaVersion`, `-WindowsTerminalVersion`, `-CmderVersion` @@ -200,13 +241,21 @@ Example: GPU profile but skip Docker and pin CUDA version ```powershell Set-ExecutionPolicy Bypass -Scope Process -Force -./scripts/provision-win.ps1 -Profile gpu -InstallDocker:$false -CudaToolkitVersion 12.4.1 +./scripts/provision-win.ps1 -Profile gpu -InstallDocker:$false +``` + +PyTorch GPU workstation with driver only: + +```powershell +./scripts/provision-win.ps1 -Profile gpu -InstallDocker:$false -InstallCUDA:$false ``` What the script does (high level) - Installs Chocolatey if missing, then uses it to install packages. Visual Studio Build Tools are installed with package parameters to include MSVC/MSBuild components. - Installs system Python first (optional), then installs Miniconda using the official latest installer by default (or Chocolatey if configured), and runs `conda init` for PowerShell and cmd. +- Installs Pixi for the current user using the official installer. +- Treats the NVIDIA driver and CUDA Toolkit as independent options. - Ensures ExecutionPolicy for the CurrentUser is set so PowerShell profiles load (required for conda init to be effective). Notes @@ -224,10 +273,12 @@ Ubuntu (new terminal): ```bash git --version +pixi --version code --version # if VS Code enabled docker --version # if Docker enabled conda --version python -V +nvidia-smi # if NVIDIA driver/CUDA enabled nvcc --version # if CUDA installed ``` @@ -235,6 +286,7 @@ macOS (new terminal): ```bash git --version +pixi --version code --version # if VS Code enabled docker --version # if Docker Desktop enabled conda --version @@ -246,10 +298,12 @@ Windows (open a new elevated PowerShell or normal PowerShell after admin tasks c ```powershell git --version +pixi --version code --version # if VS Code enabled docker --version # if Docker Desktop enabled conda --version python -V +nvidia-smi # if NVIDIA driver/CUDA enabled nvcc --version # if CUDA installed ``` @@ -259,6 +313,9 @@ nvcc --version # if CUDA installed - New shells required: Open a new terminal to pick up `conda init` changes and updated PATH. - Docker group (Ubuntu): If `docker` commands require sudo after install, log out/in or run `newgrp docker`. - VS Code apt repo conflicts: the Ubuntu script tries to detect and normalize duplicate Microsoft Code repo entries before `apt update` to avoid signed-by problems. +- CUDA package not found on Ubuntu: prefer `--cuda-toolkit-pkg=cuda-toolkit`, or inspect available pins with `apt-cache search '^cuda-toolkit-[0-9]'` after the NVIDIA repository is configured. +- PyTorch reports no GPU: run `nvidia-smi`, then check `torch.cuda.is_available()` and `torch.version.cuda` inside the same Pixi/Conda environment. The CUDA version printed by `nvidia-smi` is the driver's maximum supported API, not necessarily a system Toolkit installation. +- Pixi not on `PATH`: open a new terminal. On Ubuntu it is installed under `~/.pixi/bin`; on macOS Homebrew provides it; on Windows the official installer normally uses `%LOCALAPPDATA%\pixi\bin` and updates the user `PATH`. - Corporate proxies: Configure system proxy and git proxy settings before running the scripts. ## Uninstall/rollback (brief) diff --git a/scripts/provision-macos.sh b/scripts/provision-macos.sh index 61bf676..3d2ad0d 100644 --- a/scripts/provision-macos.sh +++ b/scripts/provision-macos.sh @@ -21,6 +21,18 @@ MINIFORGE_PREFIX="$HOME/miniforge3" INSTALL_LLVM=true # Installs llvm and libomp via Homebrew INSTALL_PYTHON=true # Homebrew Python (includes venv) +INSTALL_PIXI=true + +# CLI overrides are collected separately so profiles are applied first. +CLI_INSTALL_VSCODE="" +CLI_INSTALL_DOCKER="" +CLI_INSTALL_ITERM2="" +CLI_INSTALL_ROSETTA="" +CLI_INSTALL_MINIFORGE="" +CLI_MINIFORGE_PREFIX="" +CLI_INSTALL_LLVM="" +CLI_INSTALL_PYTHON="" +CLI_INSTALL_PIXI="" # ---------- Helpers ---------- log() { printf "\033[1;32m[INFO]\033[0m %s\n" "$*"; } @@ -35,6 +47,7 @@ need_admin() { # Bash 3.2–compatible boolean parser (lowercase via tr) parse_bool() { + local v v="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" case "$v" in true|1|yes|y|on) echo "true" ;; @@ -47,22 +60,23 @@ parse_bool() { for arg in "$@"; do case "$arg" in --profile=*) PROFILE="${arg#*=}";; - --install-vscode=*) INSTALL_VSCODE=$(parse_bool "${arg#*=}");; - --install-docker=*) INSTALL_DOCKER=$(parse_bool "${arg#*=}");; - --install-iterm2=*) INSTALL_ITERM2=$(parse_bool "${arg#*=}");; - --install-rosetta=*) INSTALL_ROSETTA=$(parse_bool "${arg#*=}");; - --install-miniforge=*) INSTALL_MINIFORGE=$(parse_bool "${arg#*=}");; - --miniforge-prefix=*) MINIFORGE_PREFIX="${arg#*=}";; - --install-llvm=*) INSTALL_LLVM=$(parse_bool "${arg#*=}");; - --install-python=*) INSTALL_PYTHON=$(parse_bool "${arg#*=}");; + --install-vscode=*) CLI_INSTALL_VSCODE=$(parse_bool "${arg#*=}");; + --install-docker=*) CLI_INSTALL_DOCKER=$(parse_bool "${arg#*=}");; + --install-iterm2=*) CLI_INSTALL_ITERM2=$(parse_bool "${arg#*=}");; + --install-rosetta=*) CLI_INSTALL_ROSETTA=$(parse_bool "${arg#*=}");; + --install-miniforge=*) CLI_INSTALL_MINIFORGE=$(parse_bool "${arg#*=}");; + --miniforge-prefix=*) CLI_MINIFORGE_PREFIX="${arg#*=}";; + --install-llvm=*) CLI_INSTALL_LLVM=$(parse_bool "${arg#*=}");; + --install-python=*) CLI_INSTALL_PYTHON=$(parse_bool "${arg#*=}");; + --install-pixi=*) CLI_INSTALL_PIXI=$(parse_bool "${arg#*=}");; -h|--help) cat <<'USAGE' Usage: bash provision-macos.sh [flags] Profiles (defaults if you omit flags): - --profile=default VS Code ON, Docker ON - --profile=minimal VS Code ON, Docker OFF - --profile=gpu VS Code ON, Docker ON (note: CUDA unsupported on macOS) + --profile=default VS Code ON, Docker ON, Pixi ON + --profile=minimal VS Code ON, Docker OFF, Pixi ON + --profile=gpu VS Code ON, Docker ON, Pixi ON (CUDA unsupported on macOS) Flags (override profile defaults): --install-vscode=true|false @@ -73,6 +87,7 @@ Flags (override profile defaults): --miniforge-prefix=/path/to/miniforge3 # default: $HOME/miniforge3 --install-llvm=true|false --install-python=true|false # Homebrew Python (with venv), default: true + --install-pixi=true|false # Pixi via Homebrew, default: true USAGE exit 0;; *) err "Unknown flag: $arg"; exit 2;; @@ -91,9 +106,25 @@ case "$PROFILE" in *) err "Invalid --profile value: $PROFILE (use default|minimal|gpu)"; exit 2;; esac +# Explicit flags override profile defaults. +[[ -n "$CLI_INSTALL_VSCODE" ]] && INSTALL_VSCODE="$CLI_INSTALL_VSCODE" +[[ -n "$CLI_INSTALL_DOCKER" ]] && INSTALL_DOCKER="$CLI_INSTALL_DOCKER" +[[ -n "$CLI_INSTALL_ITERM2" ]] && INSTALL_ITERM2="$CLI_INSTALL_ITERM2" +[[ -n "$CLI_INSTALL_ROSETTA" ]] && INSTALL_ROSETTA="$CLI_INSTALL_ROSETTA" +[[ -n "$CLI_INSTALL_MINIFORGE" ]] && INSTALL_MINIFORGE="$CLI_INSTALL_MINIFORGE" +[[ -n "$CLI_MINIFORGE_PREFIX" ]] && MINIFORGE_PREFIX="$CLI_MINIFORGE_PREFIX" +[[ -n "$CLI_INSTALL_LLVM" ]] && INSTALL_LLVM="$CLI_INSTALL_LLVM" +[[ -n "$CLI_INSTALL_PYTHON" ]] && INSTALL_PYTHON="$CLI_INSTALL_PYTHON" +[[ -n "$CLI_INSTALL_PIXI" ]] && INSTALL_PIXI="$CLI_INSTALL_PIXI" + +if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then + err "Run this script as a normal user, not with sudo; Homebrew does not support root execution." + exit 1 +fi + need_admin log "Profile: $PROFILE" -log "Flags -> VSCode:$INSTALL_VSCODE Docker:$INSTALL_DOCKER iTerm2:$INSTALL_ITERM2 Miniforge:$INSTALL_MINIFORGE LLVM:$INSTALL_LLVM Python:$INSTALL_PYTHON Rosetta:$INSTALL_ROSETTA" +log "Flags -> VSCode:$INSTALL_VSCODE Docker:$INSTALL_DOCKER iTerm2:$INSTALL_ITERM2 Miniforge:$INSTALL_MINIFORGE LLVM:$INSTALL_LLVM Python:$INSTALL_PYTHON Pixi:$INSTALL_PIXI Rosetta:$INSTALL_ROSETTA" log "Miniforge prefix -> $MINIFORGE_PREFIX" log "Architecture: $(uname -m)" @@ -182,6 +213,10 @@ if [[ "$INSTALL_PYTHON" == "true" ]]; then log "Installing Homebrew Python (includes venv)..." brew_install python fi +if [[ "$INSTALL_PIXI" == "true" ]]; then + log "Installing Pixi via Homebrew..." + brew_install pixi +fi # ---------- 4) Apps (casks) ---------- if [[ "$INSTALL_VSCODE" == "true" ]]; then @@ -209,7 +244,7 @@ fi if [[ "$INSTALL_MINIFORGE" == "true" ]]; then log "Installing Miniforge at: $MINIFORGE_PREFIX" if [[ ! -d "$MINIFORGE_PREFIX" ]]; then - tmp_inst="/tmp/miniforge.sh" + tmp_inst="$(mktemp /tmp/miniforge.XXXXXX.sh)" if is_arm; then url="https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-MacOSX-arm64.sh" else @@ -246,6 +281,13 @@ if [[ "$INSTALL_PYTHON" == "true" ]]; then core_tools="$core_tools, python (with venv)" fi echo "• Core tools: $core_tools." +if [[ "$INSTALL_PIXI" == "true" ]]; then + if command -v pixi >/dev/null 2>&1; then + echo "• Pixi: $(pixi --version)" + else + warn "Pixi was requested but is not on PATH. Open a new terminal and run 'pixi --version'." + fi +fi [[ "$INSTALL_VSCODE" == "true" ]] && echo "• VS Code installed (launch: 'Visual Studio Code')." [[ "$INSTALL_ITERM2" == "true" ]] && echo "• iTerm2 installed." [[ "$INSTALL_DOCKER" == "true" ]] && echo "• Docker Desktop installed (launch 'Docker' once to finish setup)." diff --git a/scripts/provision-ubuntu.sh b/scripts/provision-ubuntu.sh index 6887b73..eb616f2 100644 --- a/scripts/provision-ubuntu.sh +++ b/scripts/provision-ubuntu.sh @@ -18,7 +18,7 @@ trap 'on_exit' EXIT # -------- require root (re-exec with sudo) -------- if [ "${EUID:-$(id -u)}" -ne 0 ]; then - exec sudo -E bash "$0" "$@" + exec sudo bash "$0" "$@" fi # -------- defaults -------- @@ -27,9 +27,21 @@ INSTALL_VSCODE=true INSTALL_DOCKER=true INSTALL_CUDA=false INSTALL_DRIVER=false # NVIDIA driver (bare metal only; ignored in WSL) -CUDA_TOOLKIT_PKG="cuda-toolkit-12-4" +CUDA_TOOLKIT_PKG="cuda-toolkit" +NVIDIA_DRIVER_PKG="nvidia-open" INSTALL_PYTHON=true # system python3 + venv + pip +INSTALL_PIXI=true + +# CLI overrides are collected separately so profiles are applied first. +CLI_INSTALL_VSCODE="" +CLI_INSTALL_DOCKER="" +CLI_INSTALL_CUDA="" +CLI_INSTALL_DRIVER="" +CLI_CUDA_TOOLKIT_PKG="" +CLI_NVIDIA_DRIVER_PKG="" +CLI_INSTALL_PYTHON="" +CLI_INSTALL_PIXI="" # ------- parse flags (bash 4/5 compatible; no ${var,,}) ------- parse_bool() { @@ -43,31 +55,37 @@ parse_bool() { for arg in "$@"; do case "$arg" in --profile=*) PROFILE="${arg#*=}";; - --install-vscode=*) INSTALL_VSCODE=$(parse_bool "${arg#*=}");; - --install-docker=*) INSTALL_DOCKER=$(parse_bool "${arg#*=}");; - --install-cuda=*) INSTALL_CUDA=$(parse_bool "${arg#*=}");; - --install-driver=*) INSTALL_DRIVER=$(parse_bool "${arg#*=}");; - --cuda-toolkit-pkg=*) CUDA_TOOLKIT_PKG="${arg#*=}";; - --install-python=*) INSTALL_PYTHON=$(parse_bool "${arg#*=}");; + --install-vscode=*) CLI_INSTALL_VSCODE=$(parse_bool "${arg#*=}");; + --install-docker=*) CLI_INSTALL_DOCKER=$(parse_bool "${arg#*=}");; + --install-cuda=*) CLI_INSTALL_CUDA=$(parse_bool "${arg#*=}");; + --install-driver=*) CLI_INSTALL_DRIVER=$(parse_bool "${arg#*=}");; + --cuda-toolkit-pkg=*) CLI_CUDA_TOOLKIT_PKG="${arg#*=}";; + --nvidia-driver-pkg=*) CLI_NVIDIA_DRIVER_PKG="${arg#*=}";; + --install-python=*) CLI_INSTALL_PYTHON=$(parse_bool "${arg#*=}");; + --install-pixi=*) CLI_INSTALL_PIXI=$(parse_bool "${arg#*=}");; -h|--help) cat <<'USAGE' -Usage: sudo -E bash provision-ubuntu.sh [flags] +Usage: sudo bash provision-ubuntu.sh [flags] Profiles (set sensible defaults; flags override): - --profile=default VS Code ON, Docker ON - --profile=minimal VS Code ON, Docker OFF - --profile=gpu VS Code ON, Docker ON, CUDA toolkit ON (driver optional) + --profile=default VS Code ON, Docker ON, Pixi ON + --profile=minimal VS Code ON, Docker OFF, Pixi ON + --profile=gpu VS Code ON, Docker ON, Pixi ON, NVIDIA driver/toolkit ON Flags: --install-vscode=true|false --install-docker=true|false --install-cuda=true|false --install-driver=true|false - --cuda-toolkit-pkg=cuda-toolkit-12-4 + --cuda-toolkit-pkg=cuda-toolkit + --nvidia-driver-pkg=nvidia-open --install-python=true|false # python3 + venv + pip (+ python-is-python3) + --install-pixi=true|false Tips: - VERIFY_STRICT=true sudo -E bash provision-ubuntu.sh ... # make verification failures exit non-zero + sudo env VERIFY_STRICT=true bash provision-ubuntu.sh ... # make verification failures exit non-zero + Prebuilt PyTorch/Pixi environments normally need only a compatible NVIDIA + driver; use --profile=gpu --install-cuda=false when no system nvcc is needed. USAGE exit 0;; *) err "Unknown flag: $arg"; exit 2;; @@ -81,21 +99,33 @@ case "$PROFILE" in ;; gpu) INSTALL_CUDA=true + INSTALL_DRIVER=true ;; default) ;; *) err "Invalid --profile: $PROFILE (use default|minimal|gpu)"; exit 2;; esac +# Explicit flags override profile defaults. +[ -n "$CLI_INSTALL_VSCODE" ] && INSTALL_VSCODE="$CLI_INSTALL_VSCODE" +[ -n "$CLI_INSTALL_DOCKER" ] && INSTALL_DOCKER="$CLI_INSTALL_DOCKER" +[ -n "$CLI_INSTALL_CUDA" ] && INSTALL_CUDA="$CLI_INSTALL_CUDA" +[ -n "$CLI_INSTALL_DRIVER" ] && INSTALL_DRIVER="$CLI_INSTALL_DRIVER" +[ -n "$CLI_CUDA_TOOLKIT_PKG" ] && CUDA_TOOLKIT_PKG="$CLI_CUDA_TOOLKIT_PKG" +[ -n "$CLI_NVIDIA_DRIVER_PKG" ] && NVIDIA_DRIVER_PKG="$CLI_NVIDIA_DRIVER_PKG" +[ -n "$CLI_INSTALL_PYTHON" ] && INSTALL_PYTHON="$CLI_INSTALL_PYTHON" +[ -n "$CLI_INSTALL_PIXI" ] && INSTALL_PIXI="$CLI_INSTALL_PIXI" + # -------- detect env -------- IS_WSL=false if grep -qi microsoft /proc/version 2>/dev/null; then IS_WSL=true; fi # -------- target user (for user-level conda) -------- TARGET_USER="${SUDO_USER:-$(logname 2>/dev/null || echo root)}" -TARGET_HOME="$(eval echo ~"$TARGET_USER")" +TARGET_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)" +[ -n "$TARGET_HOME" ] || { err "Could not resolve home directory for user: $TARGET_USER"; exit 1; } log "Profile: $PROFILE" -log "Flags -> VSCode:$INSTALL_VSCODE Docker:$INSTALL_DOCKER CUDA:$INSTALL_CUDA Driver:$INSTALL_DRIVER ToolkitPkg:$CUDA_TOOLKIT_PKG Python:$INSTALL_PYTHON" +log "Flags -> VSCode:$INSTALL_VSCODE Docker:$INSTALL_DOCKER CUDA:$INSTALL_CUDA Driver:$INSTALL_DRIVER ToolkitPkg:$CUDA_TOOLKIT_PKG DriverPkg:$NVIDIA_DRIVER_PKG Python:$INSTALL_PYTHON Pixi:$INSTALL_PIXI" log "Installing for user: $TARGET_USER (home: $TARGET_HOME)" # -------- VS Code repo: make it canonical & conflict-free -------- @@ -158,8 +188,55 @@ setup_docker_repo() { >/etc/apt/sources.list.d/docker.list } +# -------- NVIDIA CUDA/driver repo -------- +setup_nvidia_repo() { + local distro repo_arch keyring_deb + + if $IS_WSL; then + distro="wsl-ubuntu" + else + # shellcheck disable=SC1091 + . /etc/os-release + if [ "${ID:-}" != "ubuntu" ] || [ -z "${VERSION_ID:-}" ]; then + err "CUDA repository setup supports Ubuntu only (detected ID=${ID:-unknown}, VERSION_ID=${VERSION_ID:-unknown})." + return 1 + fi + distro="ubuntu$(printf '%s' "$VERSION_ID" | tr -d '.')" + fi + + case "$(dpkg --print-architecture)" in + amd64) repo_arch="x86_64" ;; + arm64) + if $IS_WSL; then + err "The NVIDIA WSL repository is not supported on arm64 by this script." + return 1 + fi + repo_arch="sbsa" + ;; + *) + err "Unsupported architecture for the NVIDIA repository: $(dpkg --print-architecture)" + return 1 + ;; + esac + + keyring_deb="$(mktemp /tmp/cuda-keyring.XXXXXX.deb)" + log "Configuring NVIDIA repository for $distro/$repo_arch..." + if ! curl -fsSL \ + "https://developer.download.nvidia.com/compute/cuda/repos/$distro/$repo_arch/cuda-keyring_1.1-1_all.deb" \ + -o "$keyring_deb"; then + rm -f "$keyring_deb" + err "No NVIDIA repository found for $distro/$repo_arch. Check NVIDIA's supported distributions." + return 1 + fi + dpkg -i "$keyring_deb" + rm -f "$keyring_deb" + apt-get update +} + # -------- base packages + git-lfs init -------- -setup_code_repo +if [ "$INSTALL_VSCODE" = "true" ]; then + setup_code_repo +fi log "Updating apt and installing base packages..." apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y \ @@ -197,10 +274,30 @@ if [ "$INSTALL_DOCKER" = "true" ]; then fi fi -# -------- CUDA Toolkit (driver optional; in WSL2 use Windows driver + Linux toolkit) -------- +# -------- NVIDIA driver and CUDA Toolkit -------- +# The driver is a host concern. The Toolkit is optional because prebuilt +# PyTorch/Conda/Pixi packages normally supply their own CUDA user-space runtime. +if [ "$INSTALL_CUDA" = "true" ] || { [ "$INSTALL_DRIVER" = "true" ] && ! $IS_WSL; }; then + setup_nvidia_repo +fi + +REBOOT_REQUIRED=false +if [ "$INSTALL_DRIVER" = "true" ]; then + if $IS_WSL; then + warn "Skipping Linux NVIDIA driver in WSL2; install/update the NVIDIA driver on Windows." + else + log "Installing NVIDIA driver package: $NVIDIA_DRIVER_PKG" + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + "linux-headers-$(uname -r)" "$NVIDIA_DRIVER_PKG" + REBOOT_REQUIRED=true + fi +fi + if [ "$INSTALL_CUDA" = "true" ]; then - if [ "$INSTALL_DRIVER" = "true" ]; then - warn "Driver install is for bare-metal Ubuntu only. On WSL2, the NVIDIA driver must be on Windows." + if ! apt-cache show "$CUDA_TOOLKIT_PKG" >/dev/null 2>&1; then + err "CUDA toolkit package '$CUDA_TOOLKIT_PKG' is unavailable for this Ubuntu release." + err "Use --cuda-toolkit-pkg=cuda-toolkit for the latest supported release, or disable the system Toolkit." + exit 1 fi log "Installing CUDA toolkit package: $CUDA_TOOLKIT_PKG" DEBIAN_FRONTEND=noninteractive apt-get install -y "$CUDA_TOOLKIT_PKG" @@ -208,11 +305,12 @@ fi # -------- Miniconda (user-level) -------- install_miniconda_user() { - local u="$1" h="$2" + local u="$1" h="$2" group local prefix="$h/miniconda3" + group="$(id -gn "$u")" log "Installing Miniconda for user: $u" if [ ! -d "$prefix" ]; then - local url arch installer + local url arch installer tmp arch="$(uname -m)" case "$arch" in x86_64|amd64) installer="Miniconda3-latest-Linux-x86_64.sh" ;; @@ -220,9 +318,9 @@ install_miniconda_user() { *) err "Unsupported arch for Miniconda: $arch"; return 1 ;; esac url="https://repo.anaconda.com/miniconda/$installer" - tmp="/tmp/$installer" + tmp="$(mktemp "/tmp/${installer}.XXXXXX")" curl -fsSL "$url" -o "$tmp" - chown "$u":"$u" "$tmp" + chown "$u":"$group" "$tmp" sudo -H -u "$u" bash "$tmp" -b -p "$prefix" rm -f "$tmp" else @@ -244,6 +342,32 @@ install_miniconda_user() { install_miniconda_user "$TARGET_USER" "$TARGET_HOME" +# -------- Pixi (user-level) -------- +install_pixi_user() { + local u="$1" h="$2" pixi_bin="$2/.pixi/bin/pixi" installer group + if [ -x "$pixi_bin" ]; then + log "Pixi already present ($("$pixi_bin" --version 2>/dev/null))." + return 0 + fi + + log "Installing Pixi for user: $u" + installer="$(mktemp /tmp/pixi-install.XXXXXX.sh)" + curl -fsSL https://pixi.sh/install.sh -o "$installer" + group="$(id -gn "$u")" + chown "$u":"$group" "$installer" + sudo -H -u "$u" env HOME="$h" bash "$installer" + rm -f "$installer" + + if [ ! -x "$pixi_bin" ]; then + err "Pixi binary not found at $pixi_bin after installation." + return 1 + fi +} + +if [ "$INSTALL_PIXI" = "true" ]; then + install_pixi_user "$TARGET_USER" "$TARGET_HOME" +fi + # ========================= # Verification (non-strict) # ========================= @@ -302,6 +426,15 @@ else warnv "conda not found (open a NEW shell or verify Miniconda path)" fi +if [ "$INSTALL_PIXI" = "true" ]; then + PIXI_BIN="$TARGET_HOME/.pixi/bin/pixi" + if [ -x "$PIXI_BIN" ]; then + pass "pixi present ($("$PIXI_BIN" --version 2>/dev/null))" + else + warnv "pixi not found at $PIXI_BIN" + fi +fi + if [ "$INSTALL_DOCKER" = "true" ]; then if command -v docker >/dev/null 2>&1; then pass "docker present ($(docker --version 2>/dev/null))" @@ -311,6 +444,28 @@ if [ "$INSTALL_DOCKER" = "true" ]; then fi fi +if [ "$INSTALL_CUDA" = "true" ]; then + NVCC_BIN="$(command -v nvcc 2>/dev/null || true)" + if [ -z "$NVCC_BIN" ] && [ -x /usr/local/cuda/bin/nvcc ]; then + NVCC_BIN="/usr/local/cuda/bin/nvcc" + fi + if [ -n "$NVCC_BIN" ]; then + pass "nvcc present ($("$NVCC_BIN" --version | tail -n1))" + else + warnv "nvcc not found; add /usr/local/cuda/bin to PATH if the Toolkit installed successfully" + fi +fi + +if [ "$INSTALL_CUDA" = "true" ] || [ "$INSTALL_DRIVER" = "true" ]; then + if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi >/dev/null 2>&1; then + pass "NVIDIA driver operational ($(nvidia-smi --query-gpu=name,driver_version --format=csv,noheader | sed -n '1p'))" + elif $IS_WSL; then + warnv "nvidia-smi is unavailable in WSL; install/update the NVIDIA driver on Windows" + else + warnv "nvidia-smi is unavailable; a reboot may be required after driver installation" + fi +fi + if [ "$ok" -ne 0 ]; then printf "\n\033[1;33m[VERIFY]\033[0m Some items are missing or not yet on PATH.\n" [ "$VERIFY_STRICT" = "true" ] && exit 1 @@ -321,6 +476,10 @@ fi # -------- summary -------- echo log "Provisioning complete." +[ "$INSTALL_PIXI" = "true" ] && echo "• Pixi installed for $TARGET_USER: $TARGET_HOME/.pixi/bin/pixi." +[ "$INSTALL_CUDA" = "true" ] && echo "• CUDA Toolkit package installed: $CUDA_TOOLKIT_PKG." +[ "$INSTALL_DRIVER" = "true" ] && ! $IS_WSL && echo "• NVIDIA driver package installed: $NVIDIA_DRIVER_PKG." +$REBOOT_REQUIRED && warn "Reboot required to load the newly installed NVIDIA driver." echo "• Miniconda for $TARGET_USER: $TARGET_HOME/miniconda3" echo "• conda-forge enabled (strict), conda init done for bash, zsh." [ "$INSTALL_PYTHON" = "true" ] && echo "• System Python installed: python3 + venv + pip (use: 'python3 -m venv .venv')." diff --git a/scripts/provision-win.ps1 b/scripts/provision-win.ps1 index 87afb14..877fd75 100644 --- a/scripts/provision-win.ps1 +++ b/scripts/provision-win.ps1 @@ -5,6 +5,7 @@ - Installs **system Python** first (via Chocolatey) with venv/pip (toggle with -InstallPython). - Installs Miniconda using the **official latest installer by default** (toggle with -MinicondaUseDirectInstaller). * Falls back to Chocolatey if you set -MinicondaUseDirectInstaller:$false. + - Installs Pixi for the current user by default (toggle with -InstallPixi). - Configures conda-forge (strict). No envs created. - Optional CUDA prep: NVIDIA driver (optional) and CUDA Toolkit. - Ensures ExecutionPolicy (CurrentUser -> RemoteSigned) so PowerShell profile loads (conda init works). @@ -32,6 +33,7 @@ param( [bool] $InstallCmder = $true, [string] $CmderPackageId = "cmder", # "cmder" or "cmdermini" [bool] $InstallDocker = $true, + [bool] $InstallPixi = $true, # ===== System Python toggle ===== [bool] $InstallPython = $true, # Chocolatey "python" (includes venv/pip) @@ -196,8 +198,11 @@ function Install-Miniconda-Direct { "/D=$InstallDir" ) Write-Host "Running Miniconda installer (silent) to $InstallDir ..." - Start-Process -FilePath $tmp -ArgumentList $args -Wait -NoNewWindow + $installerProcess = Start-Process -FilePath $tmp -ArgumentList $args -Wait -NoNewWindow -PassThru Remove-Item $tmp -Force -ErrorAction SilentlyContinue + if ($installerProcess.ExitCode -ne 0) { + throw "Miniconda installer failed with exit code $($installerProcess.ExitCode)." + } } Write-Host "Installing Miniconda..." @@ -236,6 +241,55 @@ if ($LASTEXITCODE -ne 0) { throw "conda config add conda-forge failed." } & $condaBat init powershell & $condaBat init cmd.exe +# Pixi (current user) +function Install-Pixi { + $existing = Get-Command pixi.exe -ErrorAction SilentlyContinue + if ($existing) { + Write-Host "Pixi already installed: $($existing.Source)" + return $existing.Source + } + + $pixiBinDirs = @( + (Join-Path $env:LOCALAPPDATA 'pixi\bin'), + (Join-Path $env:USERPROFILE '.pixi\bin') + ) + $pixiExe = $pixiBinDirs | + ForEach-Object { Join-Path $_ 'pixi.exe' } | + Where-Object { Test-Path $_ } | + Select-Object -First 1 + if (-not $pixiExe) { + $installer = Join-Path $env:TEMP 'install-pixi.ps1' + Write-Host "Downloading the official Pixi installer..." + Invoke-WebRequest -Uri 'https://pixi.sh/install.ps1' -OutFile $installer -UseBasicParsing + & powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File $installer + $pixiExitCode = $LASTEXITCODE + Remove-Item $installer -Force -ErrorAction SilentlyContinue + if ($pixiExitCode -ne 0) { throw "Pixi installer failed with exit code $pixiExitCode." } + + $pixiExe = $pixiBinDirs | + ForEach-Object { Join-Path $_ 'pixi.exe' } | + Where-Object { Test-Path $_ } | + Select-Object -First 1 + } + + if (-not $pixiExe) { + throw "Pixi binary not found after installation under LOCALAPPDATA or USERPROFILE." + } + $pixiBinDir = Split-Path -Parent $pixiExe + if (($env:Path -split ';') -notcontains $pixiBinDir) { + $env:Path = "$pixiBinDir;$env:Path" + } + return $pixiExe +} + +$pixiExe = $null +if ($InstallPixi) { + $pixiExe = Install-Pixi + $pixiVersion = & $pixiExe --version + if ($LASTEXITCODE -ne 0) { throw "Pixi verification failed." } + Write-Host "Pixi installed: $pixiVersion" +} + # Ensure PowerShell profile can run try { $cur = Get-ExecutionPolicy -Scope CurrentUser -ErrorAction SilentlyContinue @@ -264,32 +318,47 @@ try { Write-Warning "Could not set ExecutionPolicy for PowerShell 7 CurrentUser: $_" } -# CUDA prep (optional) -if ($InstallCUDA) { - Write-Host "CUDA prep enabled." - if ($InstallNvidiaDriver) { - Choco-Ensure -Pkg nvidia-display-driver -Version $NvidiaDriverVersion - Write-Host "NVIDIA display driver installed (or already present). A reboot may be required." - } else { - Write-Host "Skipping NVIDIA driver install (InstallNvidiaDriver=false). Ensure a compatible driver is already installed." - } +# NVIDIA driver and CUDA Toolkit are independent. Prebuilt PyTorch/Pixi +# environments usually need only a compatible host driver. +if ($InstallNvidiaDriver) { + Choco-Ensure -Pkg nvidia-display-driver -Version $NvidiaDriverVersion + Write-Host "NVIDIA display driver installed (or already present). A reboot may be required." +} +if ($InstallCUDA) { + Write-Host "Installing CUDA Toolkit." Choco-Ensure -Pkg cuda -Version $CudaToolkitVersion # Set CUDA_PATH - $cudaRoot = Get-ChildItem "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA" -Directory -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1 + $cudaRoot = Get-ChildItem "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA" -Directory -ErrorAction SilentlyContinue | + Where-Object { $_.Name -match '^v\d+(\.\d+)+$' } | + Sort-Object { [version]($_.Name.TrimStart('v')) } -Descending | + Select-Object -First 1 if ($cudaRoot) { [Environment]::SetEnvironmentVariable('CUDA_PATH', $cudaRoot.FullName, 'Machine') $machinePath = [Environment]::GetEnvironmentVariable('Path','Machine') - $append = @("$($cudaRoot.FullName)\bin","$($cudaRoot.FullName)\libnvvp") - foreach ($p in $append) { if ($machinePath -notlike "*$p*") { $machinePath += ";" + $p } } + $append = @("$($cudaRoot.FullName)\bin") | Where-Object { Test-Path $_ } + foreach ($p in $append) { + if (($machinePath -split ';') -notcontains $p) { $machinePath += ";" + $p } + } [Environment]::SetEnvironmentVariable('Path', $machinePath, 'Machine') + $env:CUDA_PATH = $cudaRoot.FullName + $env:Path = "$($cudaRoot.FullName)\bin;$env:Path" Write-Host "Configured CUDA_PATH -> $($cudaRoot.FullName)" } else { Write-Warning "CUDA toolkit folder not found; PATH/CUDA_PATH not updated." } } +if ($InstallNvidiaDriver -or $InstallCUDA) { + $nvidiaSmi = Get-Command nvidia-smi.exe -ErrorAction SilentlyContinue + if ($nvidiaSmi) { + & $nvidiaSmi.Source --query-gpu=name,driver_version --format=csv,noheader + } else { + Write-Warning "nvidia-smi is not available in this session; reboot if the driver was just installed." + } +} + # Git defaults & git config --global core.autocrlf input & git config --global init.defaultBranch main @@ -298,6 +367,8 @@ if ($InstallCUDA) { # ----- Final status ----- $minicondaMethod = if ($MinicondaUseDirectInstaller) { 'direct' } else { 'Chocolatey' } +if ($InstallPixi) { Write-Host "Pixi installed for the current user. Open a new terminal if 'pixi' is not yet on PATH." } +if ($InstallNvidiaDriver) { Write-Host "NVIDIA driver installation requested; reboot before using GPU workloads." } Write-Host "Provisioning complete (profile: $Profile)." if ($InstallPython) { Write-Host "System Python installed (with venv & pip). Use 'py -3 -m venv .venv' or 'python -m venv .venv'." } Write-Host "Miniconda installed (via $minicondaMethod). conda-forge enabled with strict priority."