mobux 0.1.2

A touch-friendly tmux web UI for unhinged people who run terminal sessions from their phone while walking the dog
#!/usr/bin/env bash
#
# bin/setup — install the toolchain mobux needs to build.
#
# Idempotent: detects each tool and skips if already present at an adequate
# version. Safe to run repeatedly. No sudo. User-local installs only.
#
# Installs/ensures:
#   - rustup, cargo, rustc (via rustup-init if rustup is missing)
#   - rustup components: clippy, rustfmt
#   - Node.js + npm (only checked, not installed — bin/setup-twa installs Node
#     via nvm if missing)
#   - npm dependencies for the web build (`npm install`)
#
# After this script: `cargo build` and `node web/build.js` should both succeed.
set -euo pipefail

# ---------------------------------------------------------------------------
# helpers
# ---------------------------------------------------------------------------

log() { printf '\033[1;34m[setup]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[setup] WARN:\033[0m %s\n' "$*" >&2; }
fail() { printf '\033[1;31m[setup] FAIL:\033[0m %s\n' "$*" >&2; exit 1; }

# Detect repo root (parent of this script's directory).
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

# Make sure this script is executable for the next invocation.
chmod +x "${BASH_SOURCE[0]}" 2>/dev/null || true

# Source cargo env if present so a freshly-installed rustup is visible in
# this shell.
if [ -f "${HOME}/.cargo/env" ]; then
  # shellcheck disable=SC1091
  . "${HOME}/.cargo/env"
fi

# ---------------------------------------------------------------------------
# Rust toolchain
# ---------------------------------------------------------------------------

ensure_rustup() {
  if command -v rustup >/dev/null 2>&1; then
    log "rustup found: $(rustup --version 2>/dev/null | head -n1)"
    return 0
  fi

  log "rustup not found — installing via rustup-init (user-local, no sudo)"
  if ! command -v curl >/dev/null 2>&1; then
    fail "curl is required to install rustup. Install curl first."
  fi
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
    | sh -s -- -y --default-toolchain stable --no-modify-path

  if [ -f "${HOME}/.cargo/env" ]; then
    # shellcheck disable=SC1091
    . "${HOME}/.cargo/env"
  fi

  if ! command -v rustup >/dev/null 2>&1; then
    fail "rustup install reported success but rustup is not on PATH. Check ~/.cargo/bin."
  fi
}

ensure_rust_components() {
  log "Ensuring stable toolchain is installed"
  rustup toolchain install stable --profile minimal >/dev/null

  for component in clippy rustfmt; do
    if rustup component list --installed 2>/dev/null | grep -q "^${component}-"; then
      log "rustup component already installed: ${component}"
    else
      log "Installing rustup component: ${component}"
      rustup component add "${component}"
    fi
  done
}

verify_cargo_build_tools() {
  log "cargo: $(cargo --version)"
  log "rustc: $(rustc --version)"
  log "clippy: $(cargo clippy --version 2>/dev/null || echo 'MISSING')"
  log "rustfmt: $(cargo fmt --version 2>/dev/null || echo 'MISSING')"
}

# ---------------------------------------------------------------------------
# Node + npm deps for the web build
# ---------------------------------------------------------------------------

check_node() {
  if ! command -v node >/dev/null 2>&1; then
    warn "node not found. The web build (node web/build.js) will not work."
    warn "Run bin/setup-twa to install Node LTS via nvm, or install Node yourself."
    return 1
  fi
  log "node: $(node --version)"

  if ! command -v npm >/dev/null 2>&1; then
    warn "npm not found despite node being installed. Skipping npm install."
    return 1
  fi
  log "npm: $(npm --version)"
  return 0
}

install_npm_deps() {
  if [ ! -f "${REPO_ROOT}/package.json" ]; then
    warn "No package.json at ${REPO_ROOT}. Skipping npm install."
    return 0
  fi

  if [ -d "${REPO_ROOT}/node_modules" ] \
    && [ -f "${REPO_ROOT}/package-lock.json" ] \
    && [ "${REPO_ROOT}/node_modules" -nt "${REPO_ROOT}/package-lock.json" ]; then
    log "node_modules is newer than package-lock.json — skipping npm install"
    return 0
  fi

  log "Installing npm dependencies (npm ci if lockfile, else npm install)"
  if [ -f "${REPO_ROOT}/package-lock.json" ]; then
    ( cd "${REPO_ROOT}" && npm ci )
  else
    ( cd "${REPO_ROOT}" && npm install )
  fi
}

# ---------------------------------------------------------------------------
# main
# ---------------------------------------------------------------------------

log "mobux setup starting (repo root: ${REPO_ROOT})"

ensure_rustup
ensure_rust_components
verify_cargo_build_tools

if check_node; then
  install_npm_deps
fi

log "Done. You should now be able to run:"
log "  cargo build"
log "  node web/build.js   (or: make build)"

if ! command -v node >/dev/null 2>&1; then
  log ""
  log "Note: node is not on PATH. Run bin/setup-twa to install it (and the"
  log "      Android/JDK toolchain needed to build the TWA APK)."
fi