#!/usr/bin/env bash set -euo pipefail ## # toes installer # Usage: curl -fsSL https://toes.dev/install | sh # # Installs or updates toes on a Raspberry Pi. # Must be run as the 'toes' user with passwordless sudo. RELEASE_URL="https://toes.dev/release/latest.tar.gz" DEST=~/toes APPS_DIR=~/apps DATA_DIR=~/data # ── Helpers ────────────────────────────────────────────── b=$'\033[1m' d=$'\033[2m' g=$'\033[32m' c=$'\033[36m' y=$'\033[33m' r=$'\033[0m' quiet() { "$@" > /dev/null 2>&1; } info() { echo " ${d}>>${r} $1"; } fail() { echo " ${y}ERROR:${r} $1" >&2; exit 1; } # ── Preflight ──────────────────────────────────────────── echo "" echo " ${d}╔══════════════════════════════════╗${r}" echo " ${d}║${r} ${b}🐾 toes${r} ${d}- personal web appliance ║${r}" echo " ${d}╚══════════════════════════════════╝${r}" echo "" [ "$(whoami)" = "toes" ] || fail "Must be run as the 'toes' user." sudo -n true 2>/dev/null || fail "Requires passwordless sudo." # ── System packages ────────────────────────────────────── info "Updating system packages" quiet sudo apt-get update quiet sudo apt-get install -y git libcap2-bin avahi-utils fish unzip if [ "$(getent passwd toes | cut -d: -f7)" != "/usr/bin/fish" ]; then info "Setting fish as default shell" quiet sudo chsh -s /usr/bin/fish toes fi # ── Bun ────────────────────────────────────────────────── BUN="$HOME/.bun/bin/bun" if [ ! -x "$BUN" ]; then info "Installing bun" curl -fsSL https://bun.sh/install | bash > /dev/null 2>&1 [ -x "$BUN" ] || fail "bun installation failed." fi sudo ln -sf "$BUN" /usr/local/bin/bun sudo setcap 'cap_net_bind_service=+ep' "$BUN" # ── Download ───────────────────────────────────────────── info "Downloading toes" mkdir -p "$DEST" curl -fsSL "$RELEASE_URL" | tar xz --strip-components=1 -C "$DEST" # ── Directories ────────────────────────────────────────── mkdir -p "$APPS_DIR" "$DATA_DIR" "$DATA_DIR/toes" # ── Dependencies ───────────────────────────────────────── cd "$DEST" info "Installing dependencies" quiet bun install # ── Bundled apps ───────────────────────────────────────── REPOS_DIR="$DATA_DIR/repos" mkdir -p "$REPOS_DIR" info "Installing bundled apps" pids=() for app_dir in "$DEST"/apps/*/; do app=$(basename "$app_dir") [ -f "$app_dir/package.json" ] || continue echo " $app" ( cp -a "$app_dir" "$APPS_DIR/$app" quiet bun install --frozen-lockfile --cwd "$APPS_DIR/$app" || quiet bun install --cwd "$APPS_DIR/$app" ) & pids+=("$!") done for pid in "${pids[@]}"; do wait "$pid" || fail "A bundled app failed to install." done # Copy pre-built bare repos for git-based versioning cp -a "$DEST"/dist/repos/*.git "$REPOS_DIR/" # ── CLI + SSH ──────────────────────────────────────────── info "Setting up SSH access" sudo bash "$DEST/scripts/setup-ssh.sh" info "Installing CLI" sudo install -m 755 "$DEST/dist/toes" /usr/local/bin/toes # ── Systemd ────────────────────────────────────────────── info "Installing toes service" sudo install -m 644 "$DEST/scripts/toes.service" /etc/systemd/system/toes.service sudo systemctl daemon-reload sudo systemctl enable toes info "Restarting toes" sudo systemctl restart toes # ── Done ───────────────────────────────────────────────── VERSION=$(grep '"version"' "$DEST/package.json" | head -1 | sed 's/.*"version": *"\(.*\)".*/\1/') echo "" echo " ${b}${g}🐾 toes $VERSION is up!${r}" echo " ${d}─────────────────────────────${r}" echo "" echo " Dashboard: ${c}http://$(hostname).local${r}" echo " SSH CLI: ${c}ssh cli@$(hostname).local${r}" echo "" echo " ${d}Grab the CLI:${r}" echo " ${c}curl -fsSL http://$(hostname).local/install | bash${r}" echo ""