2026-08-19 20:24:56 -07:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# Build socktop + socktop_agent from source and install them.
|
|
|
|
|
#
|
|
|
|
|
# Works on Linux (x86_64, arm64/armv7, riscv64) and macOS. Handles fresh
|
|
|
|
|
# installs and upgrades; if a systemd socktop-agent service is present, its
|
|
|
|
|
# binary is replaced in place and the service restarted.
|
|
|
|
|
#
|
|
|
|
|
# ./scripts/install.sh # build HEAD of the repo you're in
|
|
|
|
|
# ./scripts/install.sh --ref v1.60.0 # build a tag/branch (clones if needed)
|
|
|
|
|
# ./scripts/install.sh --ref housekeeping-p2
|
|
|
|
|
# ./scripts/install.sh --prefix ~/.local/bin --no-service
|
|
|
|
|
#
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
REPO_URL="https://github.com/jasonwitty/socktop.git"
|
|
|
|
|
REF=""
|
|
|
|
|
PREFIX=""
|
|
|
|
|
NO_SERVICE=0
|
|
|
|
|
SRC_DIR="${SOCKTOP_SRC_DIR:-$HOME/.cache/socktop-src}"
|
|
|
|
|
|
|
|
|
|
while [ $# -gt 0 ]; do
|
|
|
|
|
case "$1" in
|
|
|
|
|
--ref) REF="$2"; shift 2 ;;
|
|
|
|
|
--prefix) PREFIX="$2"; shift 2 ;;
|
|
|
|
|
--no-service) NO_SERVICE=1; shift ;;
|
|
|
|
|
-h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
|
|
|
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
say() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
|
|
|
|
warn() { printf '\033[1;33mwarn:\033[0m %s\n' "$*" >&2; }
|
|
|
|
|
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
|
|
|
|
|
|
OS="$(uname -s)"
|
|
|
|
|
ARCH="$(uname -m)"
|
|
|
|
|
|
|
|
|
|
# ---------- toolchain ----------
|
|
|
|
|
command -v git >/dev/null || die "git is required"
|
|
|
|
|
if ! command -v cargo >/dev/null; then
|
|
|
|
|
# rustup may be installed but not on PATH in this shell
|
|
|
|
|
[ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env"
|
|
|
|
|
fi
|
|
|
|
|
if ! command -v cargo >/dev/null; then
|
|
|
|
|
say "Rust toolchain not found — installing via rustup (stable, default profile)"
|
|
|
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
|
|
|
|
|
. "$HOME/.cargo/env"
|
|
|
|
|
fi
|
|
|
|
|
command -v cc >/dev/null || warn "no C compiler found (apt: build-essential / brew: xcode-select --install) — the build may fail"
|
|
|
|
|
case "$ARCH" in
|
|
|
|
|
riscv64*)
|
|
|
|
|
# protoc-bin-vendored ships no riscv64 binary; the build falls back to
|
|
|
|
|
# the system protoc (see build.rs).
|
|
|
|
|
command -v protoc >/dev/null || die "riscv64 needs a system protoc: sudo apt install protobuf-compiler"
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
# ---------- source ----------
|
|
|
|
|
# If run from inside a socktop checkout and no --ref given, build that tree
|
|
|
|
|
# as-is (whatever is checked out, including local changes).
|
|
|
|
|
if [ -z "$REF" ] && git rev-parse --show-toplevel >/dev/null 2>&1 \
|
|
|
|
|
&& grep -qs '^name = "socktop"' "$(git rev-parse --show-toplevel)/socktop/Cargo.toml" 2>/dev/null; then
|
|
|
|
|
SRC_DIR="$(git rev-parse --show-toplevel)"
|
|
|
|
|
say "Building the current checkout: $SRC_DIR ($(git -C "$SRC_DIR" describe --always --dirty 2>/dev/null))"
|
|
|
|
|
else
|
|
|
|
|
REF="${REF:-master}"
|
|
|
|
|
if [ ! -d "$SRC_DIR/.git" ]; then
|
|
|
|
|
say "Cloning $REPO_URL -> $SRC_DIR"
|
|
|
|
|
git clone "$REPO_URL" "$SRC_DIR"
|
|
|
|
|
fi
|
|
|
|
|
say "Checking out $REF"
|
|
|
|
|
git -C "$SRC_DIR" fetch --tags origin
|
|
|
|
|
git -C "$SRC_DIR" checkout -q "$REF"
|
|
|
|
|
# fast-forward when REF is a branch
|
|
|
|
|
git -C "$SRC_DIR" merge --ff-only "origin/$REF" >/dev/null 2>&1 || true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---------- build ----------
|
|
|
|
|
say "Building release binaries (this can take a while on SBCs)"
|
|
|
|
|
( cd "$SRC_DIR" && cargo build --release -p socktop -p socktop_agent )
|
|
|
|
|
CLIENT="$SRC_DIR/target/release/socktop"
|
|
|
|
|
AGENT="$SRC_DIR/target/release/socktop_agent"
|
|
|
|
|
|
|
|
|
|
# ---------- install ----------
|
|
|
|
|
if [ -z "$PREFIX" ]; then
|
|
|
|
|
PREFIX="/usr/local/bin"
|
|
|
|
|
fi
|
|
|
|
|
SUDO=""
|
|
|
|
|
if [ ! -w "$PREFIX" ]; then
|
|
|
|
|
if command -v sudo >/dev/null; then SUDO="sudo"; else
|
|
|
|
|
PREFIX="$HOME/.local/bin"; mkdir -p "$PREFIX"
|
|
|
|
|
warn "no sudo — installing to $PREFIX (ensure it is on your PATH)"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
say "Installing to $PREFIX"
|
|
|
|
|
$SUDO install -m 755 "$CLIENT" "$PREFIX/socktop"
|
|
|
|
|
$SUDO install -m 755 "$AGENT" "$PREFIX/socktop_agent"
|
|
|
|
|
|
2026-08-20 10:23:45 -07:00
|
|
|
# Update every other copy on PATH as well. A stale `cargo install` in
|
|
|
|
|
# ~/.cargo/bin would otherwise SHADOW the fresh binary (~/.cargo/bin
|
|
|
|
|
# usually precedes /usr/local/bin on PATH), leaving `socktop --version`
|
|
|
|
|
# stuck on the old release after a "successful" install.
|
|
|
|
|
update_path_copies() {
|
|
|
|
|
local name="$1" src="$2" copy dir
|
|
|
|
|
# type -ap lists every match on PATH (bash builtin, symlinks not resolved)
|
|
|
|
|
for copy in $(type -ap "$name" | sort -u); do
|
|
|
|
|
[ "$copy" = "$PREFIX/$name" ] && continue
|
|
|
|
|
dir="$(dirname "$copy")"
|
|
|
|
|
say "Updating additional copy on PATH: $copy"
|
|
|
|
|
if [ -w "$copy" ] || [ -w "$dir" ]; then
|
|
|
|
|
install -m 755 "$src" "$copy"
|
|
|
|
|
else
|
|
|
|
|
# Non-fatal: an un-updatable extra copy shouldn't kill the install,
|
|
|
|
|
# but the user must know it may shadow the fresh binary.
|
|
|
|
|
$SUDO install -m 755 "$src" "$copy" || warn "could not update $copy — it may shadow $PREFIX/$name"
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
update_path_copies socktop "$CLIENT"
|
|
|
|
|
update_path_copies socktop_agent "$AGENT"
|
|
|
|
|
|
2026-08-19 20:24:56 -07:00
|
|
|
# ---------- systemd service (Linux only) ----------
|
|
|
|
|
if [ "$OS" = "Linux" ] && [ "$NO_SERVICE" -eq 0 ] \
|
|
|
|
|
&& command -v systemctl >/dev/null \
|
|
|
|
|
&& systemctl list-unit-files 2>/dev/null | grep -q '^socktop-agent\.service'; then
|
|
|
|
|
# The deb package's unit points at its own binary path; replace it in place
|
|
|
|
|
# so the running service picks up this build.
|
|
|
|
|
UNIT_BIN="$(systemctl show -p ExecStart socktop-agent.service 2>/dev/null \
|
|
|
|
|
| sed -n 's/.*path=\([^ ;]*\).*/\1/p' | head -1)"
|
|
|
|
|
if [ -n "$UNIT_BIN" ] && [ "$UNIT_BIN" != "$PREFIX/socktop_agent" ]; then
|
|
|
|
|
say "Refreshing systemd service binary at $UNIT_BIN"
|
|
|
|
|
$SUDO systemctl stop socktop-agent.service
|
|
|
|
|
$SUDO install -m 755 "$AGENT" "$UNIT_BIN"
|
|
|
|
|
$SUDO systemctl start socktop-agent.service
|
|
|
|
|
else
|
|
|
|
|
say "Restarting socktop-agent.service"
|
|
|
|
|
$SUDO systemctl restart socktop-agent.service
|
|
|
|
|
fi
|
|
|
|
|
sleep 1
|
|
|
|
|
systemctl --no-pager -l status socktop-agent.service | head -5 || true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
say "Installed:"
|
|
|
|
|
"$PREFIX/socktop" --version
|
|
|
|
|
"$PREFIX/socktop_agent" --version
|
2026-08-20 10:23:45 -07:00
|
|
|
say "Active on PATH: $(type -p socktop || true) / $(type -p socktop_agent || true)"
|
|
|
|
|
socktop --version
|