split the optional host dependencies out of setup.sh
8 Rust, 9 PulseAudio, 10 cliamp, 13 yt-dlp and 17 the remote desktop move to scripts/setup/setup-sidecars.sh, which nothing invokes — running it is a deliberate act. They are what the optional, sidecar-backed features need on the host, not what the app needs to serve itself. 11 Neovim, 12 the shell extras and 14 the npm globals are gone entirely. The host provisioning already installs node, npm, pm2, Claude Code, Neovim and the shell, and two installers racing for the same binaries is worse than one. That makes node, npm, pm2 and the agent CLIs prerequisites of this script rather than products of it, so the verification block still checks claude and pm2 — section 19 warns and skips rather than failing when pm2 is absent, which would otherwise finish "successfully" with nothing listening. eza is the one casualty: the provisioning installs lazygit, starship, oh-my-zsh and nvim, but not that. Section numbers keep their gaps so the two files read against each other. One line survives from the removed section 14 — the ~/.local/bin PATH export, which section 19's `has pm2` and the agent's claude lookup both still depend on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+258
@@ -0,0 +1,258 @@
|
||||
#!/bin/bash
|
||||
# Officer — host dependencies for the optional, sidecar-backed features.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/setup/setup-sidecars.sh
|
||||
#
|
||||
# WHAT THIS IS
|
||||
# Everything here was part of setup.sh and is not any more. setup.sh installs what the app needs to
|
||||
# serve itself; this installs what a handful of *optional* features need on the host, and it is never
|
||||
# invoked by setup.sh — running it is a deliberate act.
|
||||
#
|
||||
# The sections keep the numbering they had in setup.sh so the two files can be read against each
|
||||
# other:
|
||||
#
|
||||
# 1 (was 8) Rust
|
||||
# 2 (was 9) PulseAudio + audio dev headers
|
||||
# 3 (was 10) cliamp
|
||||
# 4 (was 13) yt-dlp
|
||||
# 5 (was 17) Remote desktop (delegates to setup-desktop.sh)
|
||||
#
|
||||
# There is no `light`/`full` profile here. In setup.sh these sections were the ones `light` skipped,
|
||||
# so gating them again would only mean "run this script and have it do nothing" — running it at all
|
||||
# IS the opt-in.
|
||||
#
|
||||
# ORDER MATTERS: section 3 needs Go, which setup.sh installs. Run setup.sh first.
|
||||
# Section 5 rewrites GRUB and switches the display manager — it takes effect on the next reboot.
|
||||
|
||||
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; }
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# setup.sh installed Go and rustup into the user's home and exported them for its own run only. A
|
||||
# fresh shell has neither on PATH, which would make `has go` false (silently skipping the cliamp
|
||||
# build) and `has rustc` false (re-running rustup over an existing toolchain). Put them back.
|
||||
export PATH="$HOME/.local/go/bin:$HOME/.cargo/bin:$PATH"
|
||||
|
||||
# ─── 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 — optional host dependencies ($PM)"
|
||||
echo "═══════════════════════════════════════════"
|
||||
|
||||
# ─── 1. Rust (was setup.sh section 8) ─────────────────────────────────────────
|
||||
echo ""
|
||||
echo "── Rust ──"
|
||||
|
||||
if has rustc && has cargo; then
|
||||
skip "rust ($(rustc --version 2>/dev/null | awk '{print $2}'))"
|
||||
else
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
if has rustc; then ok "rust installed"; else warn "rust install failed"; fi
|
||||
fi
|
||||
|
||||
# ─── 2. PulseAudio (headless audio for cliamp) (was section 9) ────────────────
|
||||
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
|
||||
|
||||
# Vorbis/OGG/FLAC dev headers (needed by cliamp's Go dependencies)
|
||||
case $PM in
|
||||
apt)
|
||||
for pkg in libvorbis-dev libogg-dev libflac-dev; do
|
||||
if dpkg -s "$pkg" &>/dev/null 2>&1; then skip "$pkg"; else PULSE_PKGS+=("$pkg"); fi
|
||||
done
|
||||
;;
|
||||
pacman)
|
||||
for pkg in libvorbis libogg flac; do
|
||||
if pacman -Qi "$pkg" &>/dev/null 2>&1; then skip "$pkg"; else PULSE_PKGS+=("$pkg"); fi
|
||||
done
|
||||
;;
|
||||
brew) ;; # not needed on macOS
|
||||
esac
|
||||
|
||||
if [ ${#PULSE_PKGS[@]} -gt 0 ]; then
|
||||
install_pkg "${PULSE_PKGS[@]}"
|
||||
ok "Installed: ${PULSE_PKGS[*]}"
|
||||
fi
|
||||
|
||||
# ─── 3. cliamp (music player) (was section 10) ────────────────────────────────
|
||||
echo ""
|
||||
echo "── cliamp ──"
|
||||
|
||||
# Export GOPATH (not just PATH) so `go install` lands in GOPATH_BIN — even when Go was already present
|
||||
# this run and install_go (which sets GOPATH) never ran. Otherwise go uses its default ~/go/bin and the
|
||||
# check below wrongly reports a build failure.
|
||||
export GOPATH="${GOPATH:-$HOME/.local/go-path}"
|
||||
GOPATH_BIN="$GOPATH/bin"
|
||||
export PATH="$GOPATH_BIN:$PATH"
|
||||
|
||||
if has cliamp; then
|
||||
skip "cliamp ($(command -v cliamp))"
|
||||
else
|
||||
if ! has go; then
|
||||
warn "Go not installed — skipping cliamp build (run setup.sh first)"
|
||||
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
|
||||
warn "cliamp build failed"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ─── 4. yt-dlp (video/audio download) (was section 13) ────────────────────────
|
||||
echo ""
|
||||
echo "── yt-dlp ──"
|
||||
|
||||
# Always install/upgrade via pip to get the latest version (apt repos are outdated).
|
||||
# Remove apt version first if present, then install via pip to /usr/local/bin.
|
||||
if has pip3; then
|
||||
# Remove outdated apt version if installed
|
||||
case $PM in
|
||||
apt)
|
||||
if dpkg -s yt-dlp &>/dev/null 2>&1; then
|
||||
echo " Removing outdated apt version..."
|
||||
sudo apt remove -y yt-dlp > /dev/null 2>&1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
echo " Installing/upgrading yt-dlp via pip..."
|
||||
sudo pip3 install --break-system-packages --upgrade yt-dlp 2>/dev/null
|
||||
if has yt-dlp; then ok "yt-dlp $(yt-dlp --version) installed"; else warn "yt-dlp pip install failed"; fi
|
||||
else
|
||||
case $PM in
|
||||
apt) install_pkg yt-dlp 2>/dev/null && ok "yt-dlp installed (apt — may be outdated)" || warn "yt-dlp not available" ;;
|
||||
pacman) install_pkg yt-dlp && ok "yt-dlp installed" ;;
|
||||
brew) install_pkg yt-dlp && ok "yt-dlp installed" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ─── 5. remote desktop (Ubuntu Desktop + VNC) (was section 17) ────────────────
|
||||
echo ""
|
||||
echo "── Remote Desktop (Ubuntu Desktop + VNC) ──"
|
||||
|
||||
# No "already installed" guard here on purpose. This used to skip on `dpkg -s ubuntu-desktop`, which
|
||||
# treats one package being present as proof the whole remote desktop is configured — and those are very
|
||||
# different things. A host can have ubuntu-desktop and still be missing every part that makes the mirror
|
||||
# work: GDM auto-login, the forced Xorg session, the captured EDID and its kernel command line, the
|
||||
# login-time mode setter. That was not hypothetical; it was this machine on 2026-08-02, where the guard
|
||||
# reported "skip" while five of setup-desktop.sh's steps had never run and /desktop could not survive a
|
||||
# reboot. setup-desktop.sh is idempotent throughout — every step either no-ops or is individually
|
||||
# guarded — so letting it run each time converges a partially configured host instead of trusting a
|
||||
# proxy for state it never actually checked.
|
||||
#
|
||||
# This is by far the most expensive section — it pulls the whole ubuntu-desktop meta-package, rewrites
|
||||
# /etc/default/grub and switches the display manager.
|
||||
case $PM in
|
||||
apt)
|
||||
bash "$SCRIPT_DIR/setup-desktop.sh"
|
||||
;;
|
||||
*)
|
||||
warn "Remote desktop setup is Ubuntu/Debian only — skipping"
|
||||
;;
|
||||
esac
|
||||
|
||||
# ─── verification ─────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo " Verification"
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
check() {
|
||||
if has "$1"; then ok "$1"; else fail "$1 — NOT FOUND"; fi
|
||||
}
|
||||
|
||||
echo "Rust:"
|
||||
check rustc
|
||||
check cargo
|
||||
|
||||
echo ""
|
||||
echo "Audio (cliamp):"
|
||||
check pulseaudio
|
||||
check parec
|
||||
check pactl
|
||||
check cliamp
|
||||
|
||||
echo ""
|
||||
echo "Download:"
|
||||
check yt-dlp
|
||||
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo " Done"
|
||||
echo "═══════════════════════════════════════════"
|
||||
echo ""
|
||||
echo "Notes:"
|
||||
echo " • PulseAudio null sink starts automatically with the server"
|
||||
echo " • Make sure ~/.cargo/bin is in your PATH for Rust tools"
|
||||
echo " • Make sure ~/.local/go-path/bin is in your PATH for Go-installed tools (cliamp)"
|
||||
echo " • REBOOT to switch into the GNOME-on-Xorg session the remote desktop mirrors"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user