#!/usr/bin/env bash # install.sh — TitanOcta curl-pipe bootstrap # # Usage (direct): # curl -fsSL https://titanocta.com/bootstrap | bash # # Usage (local): # bash install.sh # # What this script does: # 1. Verifies the OS (Ubuntu 22.04 / 24.04 only). # 2. Ensures python3.11+ is available; installs it if not. # 3. Ensures pip3 is available. # 4. Installs Python dependencies (rich, typer, psutil, requests). # 5. Downloads the installer bundle from the release URL (or uses a local copy). # 6. Runs install.py with its required installer/ package and any forwarded arguments. # # All arguments passed to this script are forwarded to install.py, e.g.: # curl -fsSL https://titanocta.com/bootstrap | bash -s -- --tier pro set -euo pipefail # --------------------------------------------------------------------------- # Config # --------------------------------------------------------------------------- TITANOCTA_VERSION="${TITANOCTA_VERSION:-latest}" INSTALLER_BUNDLE_URL="https://titanocta.com/titanocta-installer.tar.gz" MIN_PYTHON_MINOR=11 # 3.11+ REQUIRED_PKGS="rich typer psutil requests" # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- _log() { printf '\033[1;36m[titanocta]\033[0m %s\n' "$*"; } _ok() { printf '\033[1;32m ✓\033[0m %s\n' "$*"; } _warn() { printf '\033[1;33m ⚠\033[0m %s\n' "$*"; } _fail() { printf '\033[1;31m ✗\033[0m %s\n' "$*" >&2; } _die() { _fail "$*"; exit 1; } # --------------------------------------------------------------------------- # OS check # --------------------------------------------------------------------------- _check_os() { if [[ ! -f /etc/os-release ]]; then _warn "Cannot detect OS. Proceeding anyway (expect Ubuntu 22.04/24.04)." return fi # shellcheck disable=SC1091 source /etc/os-release if [[ "${ID:-}" != "ubuntu" ]]; then _warn "Detected OS: ${PRETTY_NAME:-unknown}. TitanOcta is tested on Ubuntu 22.04/24.04." _warn "Continuing, but unsupported OS may cause issues." else case "${VERSION_ID:-}" in "22.04"|"24.04") _ok "OS: ${PRETTY_NAME}" ;; *) _warn "Ubuntu ${VERSION_ID} is not officially supported (22.04 and 24.04 are)." _warn "Continuing anyway..." ;; esac fi } # --------------------------------------------------------------------------- # Python check / install # --------------------------------------------------------------------------- _find_python() { # Returns the first python3 binary that meets the minimum minor version. for candidate in python3.12 python3.11 python3; do if command -v "$candidate" &>/dev/null; then local minor minor=$("$candidate" -c "import sys; print(sys.version_info.minor)" 2>/dev/null || echo "0") if (( minor >= MIN_PYTHON_MINOR )); then echo "$candidate" return 0 fi fi done return 1 } _ensure_python() { if PYTHON=$(_find_python); then _ok "Python: $($PYTHON --version)" return fi _log "Python 3.${MIN_PYTHON_MINOR}+ not found. Installing via apt..." if ! command -v apt-get &>/dev/null; then _die "apt-get not found. Install Python 3.11+ manually then re-run." fi apt-get update -qq apt-get install -y python3.11 python3.11-venv python3-pip --no-install-recommends PYTHON=$(command -v python3.11) || _die "python3.11 install failed." _ok "Python installed: $($PYTHON --version)" } # --------------------------------------------------------------------------- # pip check # --------------------------------------------------------------------------- _ensure_pip() { if "$PYTHON" -m pip --version &>/dev/null; then _ok "pip available" return fi _log "pip not found. Installing..." if command -v apt-get &>/dev/null; then apt-get install -y python3-pip --no-install-recommends else curl -fsSL https://bootstrap.pypa.io/get-pip.py | "$PYTHON" fi _ok "pip installed" } # --------------------------------------------------------------------------- # Python dependencies # --------------------------------------------------------------------------- _install_deps() { _log "Installing Python dependencies: ${REQUIRED_PKGS}..." # shellcheck disable=SC2086 "$PYTHON" -m pip install --quiet --upgrade $REQUIRED_PKGS _ok "Dependencies installed" } # --------------------------------------------------------------------------- # Get installer bundle # --------------------------------------------------------------------------- _get_installer() { # If running from a curl pipe the script directory won't contain the installer package. # Try local first, fall back to downloading the release bundle. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-install.sh}")" 2>/dev/null && pwd || echo ".")" if [[ -f "${SCRIPT_DIR}/install.py" && -d "${SCRIPT_DIR}/installer" ]]; then INSTALL_PY="${SCRIPT_DIR}/install.py" _ok "Using local installer bundle" else _log "Downloading installer bundle from ${INSTALLER_BUNDLE_URL}..." INSTALL_DIR="$(mktemp -d /tmp/titanocta-installer.XXXXXX)" # Register cleanup trap 'rm -rf "${INSTALL_DIR}"' EXIT BUNDLE_PATH="${INSTALL_DIR}/titanocta-installer.tar.gz" if command -v curl &>/dev/null; then curl -fsSL "${INSTALLER_BUNDLE_URL}" -o "${BUNDLE_PATH}" elif command -v wget &>/dev/null; then wget -qO "${BUNDLE_PATH}" "${INSTALLER_BUNDLE_URL}" else _die "Neither curl nor wget found. Cannot download installer." fi tar -xzf "${BUNDLE_PATH}" -C "${INSTALL_DIR}" INSTALL_PY="${INSTALL_DIR}/titan-octa-installer/install.py" [[ -f "${INSTALL_PY}" && -d "${INSTALL_DIR}/titan-octa-installer/installer" ]] || _die "Installer bundle is incomplete." _ok "installer bundle downloaded" fi } # --------------------------------------------------------------------------- # Root check # --------------------------------------------------------------------------- _check_root() { if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then _warn "Not running as root. Systemd service installation may fail." _warn "Re-run with: sudo bash install.sh" # Don't hard-fail — user may be testing or using --dry-run else _ok "Running as root" fi } # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- main() { _log "TitanOcta Installer — bootstrap" _log "Version: ${TITANOCTA_VERSION}" echo "" _check_os _check_root _ensure_python _ensure_pip _install_deps _get_installer echo "" _log "Launching TitanOcta installer..." echo "" # Forward all arguments passed to this script on to install.py exec "$PYTHON" "${INSTALL_PY}" "$@" } main "$@"