diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..c95cdda --- /dev/null +++ b/install.sh @@ -0,0 +1,394 @@ +#!/usr/bin/env bash +# Claw Code installer +# +# Detects the host OS, verifies the Rust toolchain (rustc + cargo), +# builds the `claw` binary from the `rust/` workspace, and runs a +# post-install verification step. Supports Linux, macOS, and WSL. +# +# Usage: +# ./install.sh # debug build (fast, default) +# ./install.sh --release # optimized release build +# ./install.sh --no-verify # skip post-install verification +# ./install.sh --help # print usage +# +# Environment overrides: +# CLAW_BUILD_PROFILE=debug|release same as --release toggle +# CLAW_SKIP_VERIFY=1 same as --no-verify + +set -euo pipefail + +# --------------------------------------------------------------------------- +# Pretty printing +# --------------------------------------------------------------------------- + +if [ -t 1 ] && command -v tput >/dev/null 2>&1 && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; then + COLOR_RESET="$(tput sgr0)" + COLOR_BOLD="$(tput bold)" + COLOR_DIM="$(tput dim)" + COLOR_RED="$(tput setaf 1)" + COLOR_GREEN="$(tput setaf 2)" + COLOR_YELLOW="$(tput setaf 3)" + COLOR_BLUE="$(tput setaf 4)" + COLOR_CYAN="$(tput setaf 6)" +else + COLOR_RESET="" + COLOR_BOLD="" + COLOR_DIM="" + COLOR_RED="" + COLOR_GREEN="" + COLOR_YELLOW="" + COLOR_BLUE="" + COLOR_CYAN="" +fi + +CURRENT_STEP=0 +TOTAL_STEPS=6 + +step() { + CURRENT_STEP=$((CURRENT_STEP + 1)) + printf '\n%s[%d/%d]%s %s%s%s\n' \ + "${COLOR_BLUE}" "${CURRENT_STEP}" "${TOTAL_STEPS}" "${COLOR_RESET}" \ + "${COLOR_BOLD}" "$1" "${COLOR_RESET}" +} + +info() { printf '%s ->%s %s\n' "${COLOR_CYAN}" "${COLOR_RESET}" "$1"; } +ok() { printf '%s ok%s %s\n' "${COLOR_GREEN}" "${COLOR_RESET}" "$1"; } +warn() { printf '%s warn%s %s\n' "${COLOR_YELLOW}" "${COLOR_RESET}" "$1"; } +error() { printf '%s error%s %s\n' "${COLOR_RED}" "${COLOR_RESET}" "$1" 1>&2; } + +print_banner() { + printf '%s' "${COLOR_BOLD}" + cat <<'EOF' + ____ _ ____ _ + / ___|| | __ _ __ __ / ___|___ __| | ___ + | | | | / _` |\ \ /\ / /| | / _ \ / _` |/ _ \ + | |___ | || (_| | \ V V / | |__| (_) | (_| | __/ + \____||_| \__,_| \_/\_/ \____\___/ \__,_|\___| +EOF + printf '%s\n' "${COLOR_RESET}" + printf '%sClaw Code installer%s\n' "${COLOR_DIM}" "${COLOR_RESET}" +} + +print_usage() { + cat <<'EOF' +Usage: ./install.sh [options] + +Options: + --release Build the optimized release profile (slower, smaller binary). + --debug Build the debug profile (default, faster compile). + --no-verify Skip the post-install verification step. + -h, --help Show this help text and exit. + +Environment overrides: + CLAW_BUILD_PROFILE debug | release + CLAW_SKIP_VERIFY set to 1 to skip verification +EOF +} + +# --------------------------------------------------------------------------- +# Argument parsing +# --------------------------------------------------------------------------- + +BUILD_PROFILE="${CLAW_BUILD_PROFILE:-debug}" +SKIP_VERIFY="${CLAW_SKIP_VERIFY:-0}" + +while [ "$#" -gt 0 ]; do + case "$1" in + --release) + BUILD_PROFILE="release" + ;; + --debug) + BUILD_PROFILE="debug" + ;; + --no-verify) + SKIP_VERIFY="1" + ;; + -h|--help) + print_usage + exit 0 + ;; + *) + error "unknown argument: $1" + print_usage + exit 2 + ;; + esac + shift +done + +case "${BUILD_PROFILE}" in + debug|release) ;; + *) + error "invalid build profile: ${BUILD_PROFILE} (expected debug or release)" + exit 2 + ;; +esac + +# --------------------------------------------------------------------------- +# Troubleshooting hints +# --------------------------------------------------------------------------- + +print_troubleshooting() { + cat </dev/null 2>&1 +} + +# --------------------------------------------------------------------------- +# Step 1: detect OS / arch / WSL +# --------------------------------------------------------------------------- + +print_banner +step "Detecting host environment" + +UNAME_S="$(uname -s 2>/dev/null || echo unknown)" +UNAME_M="$(uname -m 2>/dev/null || echo unknown)" +OS_FAMILY="unknown" +IS_WSL="0" + +case "${UNAME_S}" in + Linux*) + OS_FAMILY="linux" + if grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then + IS_WSL="1" + fi + ;; + Darwin*) + OS_FAMILY="macos" + ;; + MINGW*|MSYS*|CYGWIN*) + OS_FAMILY="windows-shell" + ;; +esac + +info "uname: ${UNAME_S} ${UNAME_M}" +info "os family: ${OS_FAMILY}" +if [ "${IS_WSL}" = "1" ]; then + info "wsl: yes" +fi + +case "${OS_FAMILY}" in + linux|macos) + ok "supported platform detected" + ;; + windows-shell) + error "Detected a native Windows shell (MSYS/Cygwin/MinGW)." + error "Please re-run this script from inside a WSL distribution." + exit 1 + ;; + *) + error "Unsupported or unknown OS: ${UNAME_S}" + error "Supported: Linux, macOS, and Windows via WSL." + exit 1 + ;; +esac + +# --------------------------------------------------------------------------- +# Step 2: locate the Rust workspace +# --------------------------------------------------------------------------- + +step "Locating the Rust workspace" + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +RUST_DIR="${SCRIPT_DIR}/rust" + +if [ ! -d "${RUST_DIR}" ]; then + error "Could not find rust/ workspace next to install.sh" + error "Expected: ${RUST_DIR}" + exit 1 +fi + +if [ ! -f "${RUST_DIR}/Cargo.toml" ]; then + error "Missing ${RUST_DIR}/Cargo.toml — repository layout looks unexpected." + exit 1 +fi + +ok "workspace at ${RUST_DIR}" + +# --------------------------------------------------------------------------- +# Step 3: prerequisite checks +# --------------------------------------------------------------------------- + +step "Checking prerequisites" + +MISSING_PREREQS=0 + +if require_cmd rustc; then + RUSTC_VERSION="$(rustc --version 2>/dev/null || echo 'unknown')" + ok "rustc found: ${RUSTC_VERSION}" +else + error "rustc not found in PATH" + MISSING_PREREQS=1 +fi + +if require_cmd cargo; then + CARGO_VERSION="$(cargo --version 2>/dev/null || echo 'unknown')" + ok "cargo found: ${CARGO_VERSION}" +else + error "cargo not found in PATH" + MISSING_PREREQS=1 +fi + +if require_cmd git; then + ok "git found: $(git --version 2>/dev/null || echo 'unknown')" +else + warn "git not found — some workflows (login, session export) may degrade" +fi + +if [ "${OS_FAMILY}" = "linux" ]; then + if require_cmd pkg-config; then + ok "pkg-config found" + else + warn "pkg-config not found — may be required for OpenSSL-linked crates" + fi +fi + +if [ "${OS_FAMILY}" = "macos" ]; then + if ! require_cmd cc && ! xcode-select -p >/dev/null 2>&1; then + warn "Xcode command line tools not detected — run: xcode-select --install" + fi +fi + +if [ "${MISSING_PREREQS}" -ne 0 ]; then + error "Missing required tools. See troubleshooting below." + exit 1 +fi + +# --------------------------------------------------------------------------- +# Step 4: build the workspace +# --------------------------------------------------------------------------- + +step "Building the claw workspace (${BUILD_PROFILE})" + +CARGO_FLAGS=("build" "--workspace") +if [ "${BUILD_PROFILE}" = "release" ]; then + CARGO_FLAGS+=("--release") +fi + +info "running: cargo ${CARGO_FLAGS[*]}" +info "this may take a few minutes on the first build" + +( + cd "${RUST_DIR}" + CARGO_TERM_COLOR="${CARGO_TERM_COLOR:-always}" cargo "${CARGO_FLAGS[@]}" +) + +CLAW_BIN="${RUST_DIR}/target/${BUILD_PROFILE}/claw" + +if [ ! -x "${CLAW_BIN}" ]; then + error "Expected binary not found at ${CLAW_BIN}" + error "The build reported success but the binary is missing — check cargo output above." + exit 1 +fi + +ok "built ${CLAW_BIN}" + +# --------------------------------------------------------------------------- +# Step 5: post-install verification +# --------------------------------------------------------------------------- + +step "Verifying the installed binary" + +if [ "${SKIP_VERIFY}" = "1" ]; then + warn "verification skipped (--no-verify or CLAW_SKIP_VERIFY=1)" +else + info "running: claw --version" + if VERSION_OUT="$("${CLAW_BIN}" --version 2>&1)"; then + ok "claw --version -> ${VERSION_OUT}" + else + error "claw --version failed:" + printf '%s\n' "${VERSION_OUT}" 1>&2 + exit 1 + fi + + info "running: claw --help (smoke test)" + if "${CLAW_BIN}" --help >/dev/null 2>&1; then + ok "claw --help responded" + else + error "claw --help failed" + exit 1 + fi +fi + +# --------------------------------------------------------------------------- +# Step 6: next steps +# --------------------------------------------------------------------------- + +step "Next steps" + +cat <