364 lines
12 KiB
Bash
Executable File
364 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
# Officer — full host dependency setup
|
|
# Run once on a fresh Ubuntu/Debian host before launching the server.
|
|
# Usage: bash scripts/setup.sh
|
|
|
|
set -e
|
|
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m'
|
|
|
|
ok() { echo -e " ${GREEN}✓${NC} $1"; }
|
|
warn() { echo -e " ${YELLOW}!${NC} $1"; }
|
|
fail() { echo -e " ${RED}✗${NC} $1"; }
|
|
skip() { echo -e " - $1 (already installed)"; }
|
|
|
|
has() { command -v "$1" &>/dev/null; }
|
|
|
|
# ─── detect package manager ────────────────────────────────────────────────────
|
|
if has apt; then
|
|
PM=apt
|
|
elif has pacman; then
|
|
PM=pacman
|
|
elif has brew; then
|
|
PM=brew
|
|
else
|
|
fail "No supported package manager found (apt, pacman, brew)"
|
|
exit 1
|
|
fi
|
|
|
|
install_pkg() {
|
|
case $PM in
|
|
apt) sudo apt install -y "$@" ;;
|
|
pacman) sudo pacman -S --noconfirm "$@" ;;
|
|
brew) brew install "$@" ;;
|
|
esac
|
|
}
|
|
|
|
echo ""
|
|
echo "═══════════════════════════════════════════"
|
|
echo " Officer — dependency setup ($PM)"
|
|
echo "═══════════════════════════════════════════"
|
|
|
|
# ─── 1. core system packages ───────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── Core system packages ──"
|
|
|
|
CORE_PKGS=()
|
|
|
|
# git
|
|
if has git; then skip "git"; else CORE_PKGS+=(git); fi
|
|
|
|
# zip / unzip
|
|
if has zip; then skip "zip"; else CORE_PKGS+=(zip); fi
|
|
if has unzip; then skip "unzip"; else CORE_PKGS+=(unzip); fi
|
|
|
|
# curl / wget (usually present but just in case)
|
|
if has curl; then skip "curl"; else CORE_PKGS+=(curl); fi
|
|
|
|
# psmisc (fuser) and procps (pgrep)
|
|
if has fuser; then skip "fuser (psmisc)"; else
|
|
case $PM in
|
|
apt|pacman) CORE_PKGS+=(psmisc) ;;
|
|
brew) skip "fuser (not needed on macOS)" ;;
|
|
esac
|
|
fi
|
|
if has pgrep; then skip "pgrep (procps)"; else
|
|
case $PM in
|
|
apt) CORE_PKGS+=(procps) ;;
|
|
pacman) CORE_PKGS+=(procps-ng) ;;
|
|
brew) skip "pgrep (built-in on macOS)" ;;
|
|
esac
|
|
fi
|
|
|
|
# script (bsdutils on apt, util-linux on pacman, built-in on macOS)
|
|
if has script; then skip "script (bsdutils)"; else
|
|
case $PM in
|
|
apt) CORE_PKGS+=(bsdutils) ;;
|
|
pacman) CORE_PKGS+=(util-linux) ;;
|
|
brew) skip "script (built-in on macOS)" ;;
|
|
esac
|
|
fi
|
|
|
|
if [ ${#CORE_PKGS[@]} -gt 0 ]; then
|
|
install_pkg "${CORE_PKGS[@]}"
|
|
ok "Installed: ${CORE_PKGS[*]}"
|
|
fi
|
|
|
|
# ─── 2. archive extras (optional but useful) ──────────────────────────────────
|
|
echo ""
|
|
echo "── Archive utilities (optional) ──"
|
|
|
|
ARCHIVE_PKGS=()
|
|
|
|
# p7zip
|
|
if has 7z; then skip "7z (p7zip)"; else
|
|
case $PM in
|
|
apt) ARCHIVE_PKGS+=(p7zip-full) ;;
|
|
pacman) ARCHIVE_PKGS+=(p7zip) ;;
|
|
brew) ARCHIVE_PKGS+=(p7zip) ;;
|
|
esac
|
|
fi
|
|
|
|
# unrar
|
|
if has unrar; then skip "unrar"; else
|
|
case $PM in
|
|
apt) ARCHIVE_PKGS+=(unrar) ;;
|
|
pacman) ARCHIVE_PKGS+=(unrar) ;;
|
|
brew) ARCHIVE_PKGS+=(unrar) ;;
|
|
esac
|
|
fi
|
|
|
|
if [ ${#ARCHIVE_PKGS[@]} -gt 0 ]; then
|
|
install_pkg "${ARCHIVE_PKGS[@]}" || warn "Some archive packages may need non-free repos"
|
|
ok "Installed: ${ARCHIVE_PKGS[*]}"
|
|
fi
|
|
|
|
# ─── 3. ffmpeg ─────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── FFmpeg ──"
|
|
|
|
if has ffmpeg; then
|
|
skip "ffmpeg ($(ffmpeg -version 2>&1 | head -1 | awk '{print $3}'))"
|
|
else
|
|
install_pkg ffmpeg
|
|
ok "ffmpeg installed"
|
|
fi
|
|
|
|
# ─── 4. docker ─────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── Docker ──"
|
|
|
|
if has docker; then
|
|
skip "docker ($(docker --version 2>/dev/null | awk '{print $3}' | tr -d ','))"
|
|
else
|
|
case $PM in
|
|
apt)
|
|
warn "Installing docker.io from apt"
|
|
sudo apt install -y docker.io
|
|
sudo usermod -aG docker "$USER"
|
|
warn "You may need to log out/in for docker group to take effect"
|
|
;;
|
|
pacman)
|
|
sudo pacman -S --noconfirm docker
|
|
sudo systemctl enable --now docker
|
|
sudo usermod -aG docker "$USER"
|
|
;;
|
|
brew)
|
|
warn "Install Docker Desktop from https://www.docker.com/products/docker-desktop"
|
|
;;
|
|
esac
|
|
if has docker; then ok "docker installed"; else warn "docker not found — install manually"; fi
|
|
fi
|
|
|
|
# ─── 5. Node.js 22 ────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── Node.js 22 ──"
|
|
|
|
if has node; then
|
|
NODE_VER=$(node -v 2>/dev/null | tr -d 'v')
|
|
NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1)
|
|
if [ "$NODE_MAJOR" = "22" ]; then
|
|
skip "node v$NODE_VER"
|
|
else
|
|
warn "Node $NODE_VER found but v22 is required"
|
|
warn "Use nvm: nvm install 22 && nvm use 22"
|
|
fi
|
|
else
|
|
warn "Node.js not found — install v22 via nvm:"
|
|
warn " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash"
|
|
warn " nvm install 22"
|
|
fi
|
|
|
|
# ─── 6. Bun ───────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── Bun ──"
|
|
|
|
if has bun; then
|
|
skip "bun ($(bun --version 2>/dev/null))"
|
|
else
|
|
curl -fsSL https://bun.sh/install | bash
|
|
export BUN_INSTALL="$HOME/.bun"
|
|
export PATH="$BUN_INSTALL/bin:$PATH"
|
|
if has bun; then ok "bun installed"; else fail "bun install failed"; fi
|
|
fi
|
|
|
|
# ─── 7. Go ─────────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── Go ──"
|
|
|
|
if has go; then
|
|
skip "go ($(go version 2>/dev/null | awk '{print $3}'))"
|
|
else
|
|
install_pkg golang-go 2>/dev/null || install_pkg go 2>/dev/null || install_pkg golang 2>/dev/null
|
|
if has go; then ok "go installed"; else warn "go not found — install manually from https://go.dev/dl/"; fi
|
|
fi
|
|
|
|
# ─── 8. PulseAudio (headless audio for cliamp) ────────────────────────────────
|
|
echo ""
|
|
echo "── PulseAudio (headless audio) ──"
|
|
|
|
PULSE_PKGS=()
|
|
|
|
if has pulseaudio; then skip "pulseaudio"; else
|
|
case $PM in
|
|
apt) PULSE_PKGS+=(pulseaudio) ;;
|
|
pacman) PULSE_PKGS+=(pulseaudio) ;;
|
|
brew) warn "PulseAudio: brew install pulseaudio (cliamp audio won't work without it)" ;;
|
|
esac
|
|
fi
|
|
|
|
# pulseaudio-utils provides parec and pactl
|
|
if has parec && has pactl; then skip "pulseaudio-utils (parec, pactl)"; else
|
|
case $PM in
|
|
apt) PULSE_PKGS+=(pulseaudio-utils) ;;
|
|
pacman) ;; # included in pulseaudio package
|
|
brew) ;; # included in pulseaudio formula
|
|
esac
|
|
fi
|
|
|
|
# ALSA dev headers (needed to compile cliamp's Go audio library)
|
|
case $PM in
|
|
apt)
|
|
if dpkg -s libasound2-dev &>/dev/null 2>&1; then skip "libasound2-dev"; else PULSE_PKGS+=(libasound2-dev); fi
|
|
;;
|
|
pacman)
|
|
if pacman -Qi alsa-lib &>/dev/null 2>&1; then skip "alsa-lib"; else PULSE_PKGS+=(alsa-lib); fi
|
|
;;
|
|
brew) ;; # not needed on macOS
|
|
esac
|
|
|
|
if [ ${#PULSE_PKGS[@]} -gt 0 ]; then
|
|
install_pkg "${PULSE_PKGS[@]}"
|
|
ok "Installed: ${PULSE_PKGS[*]}"
|
|
fi
|
|
|
|
# ─── 9. cliamp (music player) ─────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── cliamp ──"
|
|
|
|
GOPATH_BIN="${GOPATH:-$HOME/go}/bin"
|
|
export PATH="$GOPATH_BIN:$PATH"
|
|
|
|
if has cliamp; then
|
|
skip "cliamp ($(which cliamp))"
|
|
else
|
|
if ! has go; then
|
|
warn "Go not installed — skipping cliamp build"
|
|
else
|
|
echo " Building cliamp from source..."
|
|
TMPDIR=$(mktemp -d)
|
|
git clone --depth=1 https://github.com/bjarneo/cliamp.git "$TMPDIR/cliamp"
|
|
(cd "$TMPDIR/cliamp" && go install .)
|
|
rm -rf "$TMPDIR"
|
|
if [ -f "$GOPATH_BIN/cliamp" ]; then
|
|
ok "cliamp installed at $GOPATH_BIN/cliamp"
|
|
else
|
|
fail "cliamp build failed"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# ─── 10. yt-dlp (optional — video/audio download) ─────────────────────────────
|
|
echo ""
|
|
echo "── yt-dlp (optional) ──"
|
|
|
|
if has yt-dlp; then
|
|
skip "yt-dlp"
|
|
else
|
|
case $PM in
|
|
apt) install_pkg yt-dlp 2>/dev/null && ok "yt-dlp installed" || warn "yt-dlp not in apt repos — install via pip or GitHub" ;;
|
|
pacman) install_pkg yt-dlp && ok "yt-dlp installed" ;;
|
|
brew) install_pkg yt-dlp && ok "yt-dlp installed" ;;
|
|
esac
|
|
fi
|
|
|
|
# ─── 11. npm dependencies ─────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "── npm global packages ──"
|
|
|
|
if has npm; then
|
|
if has pi; then
|
|
skip "pi (@mariozechner/pi-coding-agent)"
|
|
else
|
|
npm install -g @mariozechner/pi-coding-agent
|
|
if has pi; then ok "pi installed"; else warn "pi install failed"; fi
|
|
fi
|
|
else
|
|
warn "npm not found — skipping global package installs"
|
|
fi
|
|
|
|
# ─── 12. bun install (project dependencies) ───────────────────────────────────
|
|
echo ""
|
|
echo "── Project dependencies ──"
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
|
if has bun && [ -f "$PROJECT_DIR/package.json" ]; then
|
|
echo " Running bun install..."
|
|
(cd "$PROJECT_DIR" && bun install)
|
|
ok "Project dependencies installed"
|
|
else
|
|
warn "Skipping bun install (bun not found or not in project dir)"
|
|
fi
|
|
|
|
# ─── verification ─────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "═══════════════════════════════════════════"
|
|
echo " Verification"
|
|
echo "═══════════════════════════════════════════"
|
|
echo ""
|
|
|
|
check() {
|
|
if has "$1"; then ok "$1"; else fail "$1 — NOT FOUND"; fi
|
|
}
|
|
|
|
echo "Required:"
|
|
check git
|
|
check node
|
|
check bun
|
|
check npm
|
|
check docker
|
|
check ffmpeg
|
|
check zip
|
|
check script
|
|
|
|
echo ""
|
|
echo "Audio (cliamp):"
|
|
check go
|
|
check pulseaudio
|
|
check parec
|
|
check pactl
|
|
check cliamp
|
|
|
|
echo ""
|
|
echo "Optional:"
|
|
check unzip
|
|
check 7z
|
|
check unrar
|
|
check pgrep
|
|
check fuser
|
|
check yt-dlp
|
|
check pi
|
|
|
|
echo ""
|
|
echo "Installable from Settings UI:"
|
|
for tool in claude opencode; do
|
|
if has "$tool"; then ok "$tool"; else echo " - $tool (install from Settings > Applications)"; fi
|
|
done
|
|
|
|
echo ""
|
|
echo "═══════════════════════════════════════════"
|
|
echo " Setup complete!"
|
|
echo "═══════════════════════════════════════════"
|
|
echo ""
|
|
echo "Notes:"
|
|
echo " • PulseAudio null sink starts automatically with the server"
|
|
echo " • Make sure ~/go/bin is in your PATH for cliamp"
|
|
echo " • Claude, opencode, sharp, whisper-cpp, mlx-audio"
|
|
echo " can be installed from the Settings > Applications page"
|
|
echo ""
|