From 4e642dbec4c103d6618dd24cbe79a62b0ec71bad Mon Sep 17 00:00:00 2001 From: scawful Date: Sat, 20 Sep 2025 19:02:49 -0400 Subject: [PATCH] Add background agent script for YAZE with installation and execution features - Introduced a new `agent.sh` script to manage background agent functionality for YAZE on macOS and Linux. - Implemented subcommands for installation, uninstallation, one-shot execution, and a watch mode using inotify. - Added support for environment variable configuration and platform-specific installation procedures. - Enhanced user experience with detailed usage instructions and logging for build and test processes. --- scripts/agent.sh | 308 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100755 scripts/agent.sh diff --git a/scripts/agent.sh b/scripts/agent.sh new file mode 100755 index 00000000..bf4857f8 --- /dev/null +++ b/scripts/agent.sh @@ -0,0 +1,308 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Unified background agent script for YAZE (macOS launchd, Linux systemd) +# Subcommands: +# install [--build-type X] [--use-inotify] [--ubuntu-bootstrap] [--enable-linger] +# uninstall +# run-once # one-shot build & test +# watch # linux: inotify-based watch loop + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +BUILD_DIR_DEFAULT="${PROJECT_ROOT}/build" +BUILD_TYPE_DEFAULT="Debug" + +usage() { + cat < [options] + +Subcommands: + install Install background agent + --build-type X CMake build type (default: ${BUILD_TYPE_DEFAULT}) + --use-inotify Linux: use long-lived inotify watcher + --ubuntu-bootstrap Install Ubuntu deps via apt (sudo) + --enable-linger Enable user lingering (Linux) + + uninstall Remove installed background agent + + run-once One-shot build and test + env: BUILD_DIR, BUILD_TYPE + + watch Linux: inotify-based watch loop + env: BUILD_DIR, BUILD_TYPE, INTERVAL_SECONDS (fallback) + +Environment: + BUILD_DIR Build directory (default: ${BUILD_DIR_DEFAULT}) + BUILD_TYPE CMake build type (default: ${BUILD_TYPE_DEFAULT}) +EOF +} + +build_and_test() { + local build_dir="${BUILD_DIR:-${BUILD_DIR_DEFAULT}}" + local build_type="${BUILD_TYPE:-${BUILD_TYPE_DEFAULT}}" + local log_file="${LOG_FILE:-"${build_dir}/yaze_agent.log"}" + + mkdir -p "${build_dir}" + { + echo "==== $(date '+%Y-%m-%d %H:%M:%S') | yaze agent run (type=${build_type}) ====" + echo "Project: ${PROJECT_ROOT}" + + cmake -S "${PROJECT_ROOT}" -B "${build_dir}" -DCMAKE_BUILD_TYPE="${build_type}" + cmake --build "${build_dir}" --config "${build_type}" -j + + if [[ -x "${build_dir}/bin/yaze_test" ]]; then + "${build_dir}/bin/yaze_test" + else + if command -v ctest >/dev/null 2>&1; then + ctest --test-dir "${build_dir}" --output-on-failure + else + echo "ctest not found and test binary missing. Skipping tests." >&2 + exit 1 + fi + fi + } >>"${log_file}" 2>&1 +} + +sub_run_once() { + build_and_test +} + +sub_watch() { + local build_dir="${BUILD_DIR:-${BUILD_DIR_DEFAULT}}" + local build_type="${BUILD_TYPE:-${BUILD_TYPE_DEFAULT}}" + local interval="${INTERVAL_SECONDS:-5}" + mkdir -p "${build_dir}" + if command -v inotifywait >/dev/null 2>&1; then + echo "[agent] using inotifywait watcher" + while true; do + BUILD_DIR="${build_dir}" BUILD_TYPE="${build_type}" build_and_test || true + inotifywait -r -e modify,create,delete,move \ + "${PROJECT_ROOT}/src" \ + "${PROJECT_ROOT}/test" \ + "${PROJECT_ROOT}/CMakeLists.txt" \ + "${PROJECT_ROOT}/src/CMakeLists.txt" >/dev/null 2>&1 || true + done + else + echo "[agent] inotifywait not found; running periodic loop (${interval}s)" + while true; do + BUILD_DIR="${build_dir}" BUILD_TYPE="${build_type}" build_and_test || true + sleep "${interval}" + done + fi +} + +ensure_exec() { + if [[ ! -x "$1" ]]; then + chmod +x "$1" + fi +} + +ubuntu_bootstrap() { + if command -v apt-get >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y inotify-tools build-essential cmake ninja-build pkg-config \ + libsdl2-dev libpng-dev libglew-dev libwavpack-dev libabsl-dev \ + libboost-all-dev libboost-python-dev python3-dev libpython3-dev + else + echo "apt-get not found; skipping Ubuntu bootstrap" >&2 + fi +} + +enable_linger_linux() { + if command -v loginctl >/dev/null 2>&1; then + if sudo -n true 2>/dev/null; then + sudo loginctl enable-linger "$USER" || true + else + echo "Note: enabling linger may require sudo: sudo loginctl enable-linger $USER" >&2 + fi + fi +} + +install_macos() { + local build_type="${1}" + local build_dir="${2}" + local label="com.yaze.watchtest" + local plist_path="$HOME/Library/LaunchAgents/${label}.plist" + + mkdir -p "${build_dir}" + + cat >"$plist_path" < + + + + Label + ${label} + RunAtLoad + + ProgramArguments + + /bin/zsh + -lc + "${SCRIPT_DIR}/agent.sh" run-once + + WorkingDirectory + ${PROJECT_ROOT} + WatchPaths + + ${PROJECT_ROOT}/src + ${PROJECT_ROOT}/test + ${PROJECT_ROOT}/CMakeLists.txt + ${PROJECT_ROOT}/src/CMakeLists.txt + + StandardOutPath + ${build_dir}/yaze_agent.out.log + StandardErrorPath + ${build_dir}/yaze_agent.err.log + EnvironmentVariables + + BUILD_TYPE${build_type} + BUILD_DIR${build_dir} + + + +PLIST + + launchctl unload -w "$plist_path" 2>/dev/null || true + launchctl load -w "$plist_path" + echo "LaunchAgent installed and loaded: ${plist_path}" +} + +install_linux() { + local build_type="${1}" + local build_dir="${2}" + local use_inotify="${3}" + local systemd_dir="$HOME/.config/systemd/user" + local service_name="yaze-watchtest.service" + local path_name="yaze-watchtest.path" + + mkdir -p "${systemd_dir}" "${build_dir}" + + if [[ "${use_inotify}" == "1" ]]; then + cat >"${systemd_dir}/yaze-watchtest-inotify.service" <"${systemd_dir}/${service_name}" <"${systemd_dir}/${path_name}" <&2; usage; exit 1 ;; + esac + done + + case "$(uname -s)" in + Darwin) + install_macos "${build_type}" "${build_dir}" ;; + Linux) + if (( do_bootstrap == 1 )); then ubuntu_bootstrap; fi + if (( do_linger == 1 )); then enable_linger_linux; fi + install_linux "${build_type}" "${build_dir}" "${use_inotify}" ;; + *) + echo "Unsupported platform" >&2; exit 1 ;; + esac +} + +sub_uninstall() { + case "$(uname -s)" in + Darwin) + local label="com.yaze.watchtest" + local plist_path="$HOME/Library/LaunchAgents/${label}.plist" + launchctl unload -w "$plist_path" 2>/dev/null || true + rm -f "$plist_path" + echo "Removed LaunchAgent ${label}" + ;; + Linux) + local systemd_dir="$HOME/.config/systemd/user" + systemctl --user stop yaze-watchtest.path 2>/dev/null || true + systemctl --user disable yaze-watchtest.path 2>/dev/null || true + systemctl --user stop yaze-watchtest.service 2>/dev/null || true + systemctl --user stop yaze-watchtest-inotify.service 2>/dev/null || true + systemctl --user disable yaze-watchtest-inotify.service 2>/dev/null || true + rm -f "${systemd_dir}/yaze-watchtest.service" "${systemd_dir}/yaze-watchtest.path" "${systemd_dir}/yaze-watchtest-inotify.service" + systemctl --user daemon-reload || true + echo "Removed systemd user units" + ;; + *) echo "Unsupported platform" >&2; exit 1 ;; + esac +} + +main() { + ensure_exec "$0" + local subcmd="${1:-}"; shift || true + case "${subcmd}" in + install) sub_install "$@" ;; + uninstall) sub_uninstall ;; + run-once) sub_run_once ;; + watch) sub_watch ;; + -h|--help|help|"") usage ;; + *) echo "Unknown subcommand: ${subcmd}" >&2; usage; exit 1 ;; + esac +} + +main "$@" + +