#!/usr/bin/env bash # Install or upgrade CST-Agent on Linux, WSL or macOS. Safe to rerun: an # existing checkout is fast-forwarded, rebuilt and reinstalled. # # bootstrap_linux.sh [DIR] [--no-pull] [--release] [--source] # cst-agent-update (installed by this script; same as rerunning it) # # --release downloads the static musl tarball for this host (fast; no compile). # --source (default) builds from the checkout. CST_INSTALL_MODE=release|source # overrides the flag. # # Env: CST_AGENT_PREFIX (install prefix, default ~/.local), CST_AGENT_DIR # (checkout, default: current checkout or ~/CST-agent), CST_AGENT_REPO, # CST_AGENT_VERSION (pin a release; default: cst_version.h or latest tag). set -euo pipefail ROOT_HINT="$(cd "$(dirname "$0")/.." 2>/dev/null && pwd || true)" deps_hint() { echo "==> deps: gcc/clang make pkg-config libcurl ncursesw" if [[ "$(uname -s)" == Darwin ]]; then echo " macOS: xcode-select --install && brew install ncurses curl pkg-config" elif command -v pacman >/dev/null 2>&1; then echo " Arch: sudo pacman -S --needed base-devel curl ncurses" elif command -v apt-get >/dev/null 2>&1; then echo " Debian/Ubuntu/WSL: sudo apt install build-essential pkg-config libcurl4-openssl-dev libncurses-dev" elif command -v dnf >/dev/null 2>&1; then echo " Fedora: sudo dnf install gcc make pkgconf libcurl-devel ncurses-devel" elif command -v apk >/dev/null 2>&1; then echo " Alpine: sudo apk add build-base pkgconf curl-dev ncurses-dev" fi } # On NixOS (outside a nix shell) build inside shell.nix. run_make() { if [[ -z "${IN_NIX_SHELL:-}" ]] && command -v nix-shell >/dev/null 2>&1 && [[ -f shell.nix ]]; then nix-shell --run "make $*" else make "$@" fi } install_bin() { local prefix="$1" bin="$2" mkdir -p "$prefix/bin" install -m 0755 "$bin" "$prefix/bin/cst-agent" } install_helpers() { local prefix="$1" dir="$2" mkdir -p "$prefix/bin" cat >"$prefix/bin/cst-agent-update" <"$prefix/bin/cst-agent-service" </dev/null 2>&1 && systemctl --user is-active cst-agent-parliament.service >/dev/null 2>&1; then systemctl --user status cst-agent-parliament.service --no-pager else "\$BIN" --service-stop >/dev/null 2>&1 || true echo "parliament service: not managed by systemd --user (use --login / --service)" fi ;; install-unit) make -C "$dir" install-service PREFIX="$prefix" ;; start) systemctl --user start cst-agent-parliament.service ;; stop) systemctl --user stop cst-agent-parliament.service; "\$BIN" --service-stop || true ;; restart) systemctl --user restart cst-agent-parliament.service ;; *) echo "usage: cst-agent-service {status|install-unit|start|stop|restart}" >&2 exit 2 ;; esac EOF chmod 0755 "$prefix/bin/cst-agent-service" } # Fast path: fetch static release tarball for this machine. install_from_release() { local prefix="$1" dir="$2" # shellcheck source=lib_release.sh . "$dir/scripts/lib_release.sh" ROOT="$dir" local arch arch="$(cst_host_release_arch)" || { echo "no prebuilt release for $(uname -m); use --source" >&2 return 1 } local ver="${CST_AGENT_VERSION:-}" if [[ -z "$ver" ]]; then ver="$(cst_version)" fi local name="cst-agent-$ver-linux-$arch.tar.gz" local tmp tmp="$(mktemp -d /tmp/cst-rel.XXXXXX)" # shellcheck disable=SC2046 if ! cst_fetch "$tmp/$name" $(cst_release_asset_urls "$ver" "$name"); then echo "release download failed for $name; falling back to source build" >&2 rm -rf "$tmp" return 1 fi tar -C "$tmp" -xzf "$tmp/$name" local bin bin="$(find "$tmp" -type f -name cst-agent | head -1)" [[ -x "$bin" ]] || { echo "tarball missing cst-agent" >&2; rm -rf "$tmp"; return 1; } install_bin "$prefix" "$bin" rm -rf "$tmp" echo "==> installed release $ver ($arch) -> $prefix/bin/cst-agent" } main() { local repo_url="${CST_AGENT_REPO:-https://git.whcself.xyz/WHC/CST-agent.git}" local prefix="${CST_AGENT_PREFIX:-$HOME/.local}" local dir="" pull=1 changed=0 mode="${CST_INSTALL_MODE:-source}" arg for arg in "$@"; do case "$arg" in --no-pull) pull=0 ;; --release) mode=release ;; --source) mode=source ;; --rebuilt) changed=1 ;; # internal: re-exec after a pull -h | --help) sed -n '2,16p' "$0" return 0 ;; *) dir="$arg" ;; esac done if [[ -z "$dir" ]]; then if [[ -n "${CST_AGENT_DIR:-}" ]]; then dir="$CST_AGENT_DIR" elif [[ -f Makefile && -f src/main.c && -d .git ]]; then dir="$(pwd)" elif [[ -n "$ROOT_HINT" && -f "$ROOT_HINT/Makefile" ]]; then dir="$ROOT_HINT" else dir="$HOME/CST-agent" fi fi local old="" new="" if [[ ! -f "$dir/Makefile" ]]; then echo "==> git clone $repo_url -> $dir" git clone "$repo_url" "$dir" changed=1 elif ((pull)); then old="$(git -C "$dir" rev-parse HEAD)" if git -C "$dir" diff --quiet && git -C "$dir" diff --cached --quiet; then echo "==> git pull ($dir)" git -C "$dir" pull --ff-only else echo "warning: $dir has local changes; skipping git pull" >&2 fi new="$(git -C "$dir" rev-parse HEAD)" if [[ "$old" != "$new" ]]; then echo " $(git -C "$dir" log --oneline "$old..$new" | wc -l) new commit(s)" # The script itself may have changed: continue with the new version. exec bash "$dir/scripts/bootstrap_linux.sh" "$dir" --no-pull --rebuilt \ ${mode:+--$mode} fi fi cd "$dir" dir="$(pwd)" if [[ "$mode" == release ]]; then if install_from_release "$prefix" "$dir"; then install_helpers "$prefix" "$dir" echo echo "OK: $("$prefix/bin/cst-agent" --version) (release install)" case ":$PATH:" in *":$prefix/bin:"*) ;; *) echo "Add to PATH: export PATH=\"$prefix/bin:\$PATH\"" ;; esac echo "Upgrade: cst-agent-update --release Rebuild from source: --source" return 0 fi echo "==> continuing with source build" fi local jobs jobs="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)" echo "==> build (incremental, -j$jobs)" # Prefer incremental rebuilds. Set CST_AGENT_CLEAN=1 to force a clean tree. if [[ "${CST_AGENT_CLEAN:-0}" == "1" ]]; then run_make clean >/dev/null 2>&1 || true fi : "${changed:=0}" if ! run_make -j"$jobs"; then deps_hint return 1 fi if [[ "${CST_AGENT_SKIP_CHECK:-0}" != "1" ]]; then run_make -j"$jobs" check >/dev/null 2>&1 || echo "warning: some tests failed (run 'make check')" fi echo "==> install to $prefix/bin" install_bin "$prefix" "$dir/cst-agent" install_helpers "$prefix" "$dir" echo echo "OK: $("$prefix/bin/cst-agent" --version)" case ":$PATH:" in *":$prefix/bin:"*) ;; *) echo "Add to PATH: export PATH=\"$prefix/bin:\$PATH\"" ;; esac echo "Run 'cst-agent' inside a project directory; upgrade with 'cst-agent-update'." echo "Fast upgrade from Release: 'cst-agent-update --release'." echo "Parliament seat service helper: 'cst-agent-service status|install-unit'." echo "The first start asks for the provider and its API key and saves them." } main "$@"