#!/bin/sh # Served from https://dotfiles.bolte.sh/install.sh. Build embeds immutable payload metadata. set -eu bolte_release='480518d3fecedee0520a' bolte_sha256='480518d3fecedee0520a3d0f8d286cc1dea5407b277a52a1ff5503624ccf41e6' bolte_base='https://dotfiles.bolte.sh' bolte_runtime_consent=0 bolte_no_input=0 bolte_dry_run=0 bolte_target_home=${HOME:-} bolte_home_next=0 for bolte_argument in "$@"; do if [ "$bolte_home_next" = 1 ]; then bolte_target_home=$bolte_argument bolte_home_next=0 fi case "$bolte_argument" in --home) bolte_home_next=1 ;; --home=*) bolte_target_home=${bolte_argument#--home=} ;; --bootstrap-python) bolte_runtime_consent=1 ;; --no-input|--json) bolte_no_input=1 ;; --dry-run|-n) bolte_dry_run=1 ;; --help|-h) cat <<'HELP' Bolte dotfiles — a familiar environment for each machine. curl -fsSL https://dotfiles.bolte.sh/install.sh -o /tmp/bolte-install.sh sh /tmp/bolte-install.sh --features shell --dry-run sh /tmp/bolte-install.sh --features shell,git,vim,tmux --backup-conflicts --yes Choose individual features; start with shell and add only what you need. Compose with --features shell,git; customize with --with / --without FEATURE. Features: shell, git, vim, tmux, terminal, dev, mono, wiki, packages. --features FEATURES Exact selection; repeat or comma-separate (none clears) --feature FEATURE Alias for --features --dry-run Preview files and configuration; make no home changes --yes --no-input Apply without prompts --backup-conflicts Preserve existing files before replacing them --migrate-from PATH Replace links into a legacy dotfiles checkout --home PATH Use a different installation home --json / --no-color Machine output / plain terminal output --run-hook Explicitly run your private ~/.bolte/post-install.sh --skip-hook Skip the optional private-hook prompt --bootstrap-python Explicitly allow a private runtime if Python is absent Python 3.9+ is sufficient. Without Python, an interactive prompt can install uv and Python privately under ~/.bolte/runtime. No sudo or shell edits are used. After installation: bolte-dotfiles status, install, list, uninstall. HELP exit 0 ;; esac done if [ -z "$bolte_target_home" ]; then printf '%s\n' 'No home directory is configured. Set HOME or pass --home PATH.' >&2 exit 1 fi case "$bolte_release" in @*) printf '%s\n' 'This is the source template. Run ./dotfiles/install from the repository.' >&2 exit 1 ;; esac bolte_temp=$(mktemp -d "${TMPDIR:-/tmp}/bolte-install.XXXXXXXX") trap 'rm -rf "$bolte_temp"' EXIT HUP INT TERM bolte_python='' for bolte_candidate in "${BOLTE_PYTHON:-python3}" python3.13 python3.12 python3.11 "$bolte_target_home"/.bolte/runtime/python/*/bin/python3; do if command -v "$bolte_candidate" >/dev/null 2>&1 && "$bolte_candidate" -c 'import sys; sys.exit(sys.version_info < (3, 9))' 2>/dev/null; then bolte_python=$(command -v "$bolte_candidate") break fi done if [ -z "$bolte_python" ]; then if [ "$bolte_dry_run" = 1 ]; then printf '%s\n' 'Python 3.9+ is needed for this preview. No runtime has been installed.' >&2 exit 1 fi if [ "$bolte_runtime_consent" = 0 ] && [ "$bolte_no_input" = 0 ] && ( : /dev/null; then printf '%s\n' 'Python is missing. Install uv and Python privately in ~/.bolte/runtime?' 'This downloads from astral.sh and GitHub, uses no sudo, and leaves shell startup unchanged.' >&2 printf '%s' 'Install private runtime? [y/N] ' >&2 read -r bolte_answer &2 exit 1 fi if [ -L "$bolte_target_home/.bolte" ] || [ -L "$bolte_target_home/.bolte/runtime" ] || [ -L "$bolte_target_home/.bolte/runtime/bin" ] || [ -L "$bolte_target_home/.bolte/runtime/python" ] || [ -L "$bolte_target_home/.bolte/runtime/cache" ]; then printf '%s\n' 'Refusing a symlinked ~/.bolte runtime directory.' >&2 exit 1 fi umask 077 mkdir -p "$bolte_target_home/.bolte/runtime/bin" curl --proto '=https' --tlsv1.2 --fail --show-error --silent --location --connect-timeout 15 --max-time 120 https://astral.sh/uv/install.sh -o "$bolte_temp/uv-install.sh" UV_NO_MODIFY_PATH=1 UV_UNMANAGED_INSTALL="$bolte_target_home/.bolte/runtime/bin" sh "$bolte_temp/uv-install.sh" PATH="$bolte_target_home/.bolte/runtime/bin:$PATH" UV_PYTHON_INSTALL_DIR="$bolte_target_home/.bolte/runtime/python" UV_PYTHON_BIN_DIR="$bolte_target_home/.bolte/runtime/bin" UV_CACHE_DIR="$bolte_target_home/.bolte/runtime/cache" "$bolte_target_home/.bolte/runtime/bin/uv" python install 3.12 bolte_python=$(UV_PYTHON_INSTALL_DIR="$bolte_target_home/.bolte/runtime/python" UV_CACHE_DIR="$bolte_target_home/.bolte/runtime/cache" "$bolte_target_home/.bolte/runtime/bin/uv" python find --managed-python 3.12) fi printf '%s\n' 'Downloading the verified Bolte shell environment…' >&2 curl --proto '=https' --tlsv1.2 --fail --show-error --silent --location --connect-timeout 15 --max-time 120 "$bolte_base/releases/$bolte_release/dotfiles.tar.gz" -o "$bolte_temp/dotfiles.tar.gz" "$bolte_python" - "$bolte_temp" "$bolte_sha256" <<'PY' import hashlib from pathlib import Path import sys import tarfile root = Path(sys.argv[1]) archive = root / 'dotfiles.tar.gz' if hashlib.sha256(archive.read_bytes()).hexdigest() != sys.argv[2]: raise SystemExit('Payload checksum mismatch; nothing has been installed. Try downloading the installer again.') with tarfile.open(archive) as bundle: members = bundle.getmembers() for member in members: if not member.isfile() or member.name.startswith('/') or '..' in Path(member.name).parts: raise SystemExit('Unexpected payload entry; installation stopped.') if sys.version_info >= (3, 12): bundle.extractall(root / 'payload', members=members, filter='data') else: bundle.extractall(root / 'payload', members=members) PY # curl | sh consumes stdin; hand only the downloaded installer a terminal when one exists. # --no-input/--json intentionally keep stdin untouched for automation. if [ "$bolte_no_input" = 0 ] && [ ! -t 0 ] && ( : /dev/null; then BOLTE_PYTHON="$bolte_python" sh "$bolte_temp/payload/install" "$@"