Replace monolithic install.sh with modular script architecture
- Remove old single-file installer - Add worker scripts with JSON event streaming protocol: - scripts/prepare.sh: system prep, Docker/Compose installation - scripts/strix.sh: deploy Strix standalone via Docker Compose - scripts/strix-frigate.sh: deploy Strix + Frigate with HW autodetect - scripts/proxmox-lxc-create.sh: create Ubuntu LXC on Proxmox - Add scripts/install.sh: animated frontend with owl display - Update docker-compose.frigate.yml: host networking, internal API port, expanded device comments, GPU image hints
This commit is contained in:
Executable
+274
@@ -0,0 +1,274 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# Strix -- strix.sh (worker)
|
||||
#
|
||||
# Deploys Strix container via Docker Compose.
|
||||
# Downloads docker-compose.yml from GitHub (if not already present),
|
||||
# generates .env from parameters, pulls image, starts container, healthchecks.
|
||||
#
|
||||
# Protocol:
|
||||
# - Every action is reported as a single-line JSON to stdout.
|
||||
# - Types: check, ok, miss, install, error, done
|
||||
# - Field "msg" is always human-readable.
|
||||
# - Field "data" is optional, carries machine-readable details.
|
||||
# - Last line is always: {"type":"done","ok":true,...} or {"type":"done","ok":false,"error":"..."}
|
||||
# - Exit code: 0 = success, 1 = failure.
|
||||
#
|
||||
# Parameters (all optional):
|
||||
# --port PORT Strix listen port (default: 4567)
|
||||
# --frigate-url URL Frigate URL, e.g. http://192.168.1.50:5000
|
||||
# --go2rtc-url URL go2rtc URL, e.g. http://192.168.1.50:1984
|
||||
# --log-level LEVEL Log level: debug, info, warn, error, trace (default: info)
|
||||
# --tag TAG Docker image tag (default: latest)
|
||||
# --dir DIR Working directory (default: /opt/strix)
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/strix.sh
|
||||
# bash scripts/strix.sh --port 4567 --frigate-url http://192.168.1.50:5000
|
||||
# =============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Defaults
|
||||
# ---------------------------------------------------------------------------
|
||||
STRIX_PORT="4567"
|
||||
STRIX_FRIGATE_URL=""
|
||||
STRIX_GO2RTC_URL=""
|
||||
STRIX_LOG_LEVEL=""
|
||||
STRIX_TAG="latest"
|
||||
STRIX_DIR="/opt/strix"
|
||||
COMPOSE_URL="https://raw.githubusercontent.com/eduard256/Strix/main/docker-compose.yml"
|
||||
IMAGE="eduard256/strix"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Parse CLI arguments
|
||||
# ---------------------------------------------------------------------------
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--port) STRIX_PORT="$2"; shift 2 ;;
|
||||
--frigate-url) STRIX_FRIGATE_URL="$2"; shift 2 ;;
|
||||
--go2rtc-url) STRIX_GO2RTC_URL="$2"; shift 2 ;;
|
||||
--log-level) STRIX_LOG_LEVEL="$2"; shift 2 ;;
|
||||
--tag) STRIX_TAG="$2"; shift 2 ;;
|
||||
--dir) STRIX_DIR="$2"; shift 2 ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# JSON helpers (same protocol as prepare.sh)
|
||||
# ---------------------------------------------------------------------------
|
||||
emit() {
|
||||
local type="$1"
|
||||
local msg="$2"
|
||||
local data="${3:-}"
|
||||
|
||||
msg="${msg//\\/\\\\}"
|
||||
msg="${msg//\"/\\\"}"
|
||||
|
||||
if [[ -n "$data" ]]; then
|
||||
printf '{"type":"%s","msg":"%s","data":%s}\n' "$type" "$msg" "$data"
|
||||
else
|
||||
printf '{"type":"%s","msg":"%s"}\n' "$type" "$msg"
|
||||
fi
|
||||
}
|
||||
|
||||
emit_done() {
|
||||
local ok="$1"
|
||||
shift
|
||||
|
||||
if [[ "$ok" == "true" ]]; then
|
||||
# Remaining args are key:value pairs for data
|
||||
local data="{"
|
||||
local first=true
|
||||
while [[ $# -ge 2 ]]; do
|
||||
local key="$1" val="$2"; shift 2
|
||||
val="${val//\\/\\\\}"
|
||||
val="${val//\"/\\\"}"
|
||||
[[ "$first" == true ]] && first=false || data="${data},"
|
||||
data="${data}\"${key}\":\"${val}\""
|
||||
done
|
||||
data="${data}}"
|
||||
printf '{"type":"done","ok":true,"data":%s}\n' "$data"
|
||||
exit 0
|
||||
else
|
||||
local error="${1:-unknown}"
|
||||
error="${error//\\/\\\\}"
|
||||
error="${error//\"/\\\"}"
|
||||
printf '{"type":"done","ok":false,"error":"%s"}\n' "$error"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Detect LAN IP
|
||||
# ---------------------------------------------------------------------------
|
||||
detect_lan_ip() {
|
||||
local ip=""
|
||||
ip=$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'src \K\S+' | head -1)
|
||||
[[ -z "$ip" ]] && ip=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
[[ -z "$ip" ]] && ip="localhost"
|
||||
echo "$ip"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Working directory
|
||||
# ---------------------------------------------------------------------------
|
||||
setup_dir() {
|
||||
emit "check" "Checking working directory ${STRIX_DIR}"
|
||||
|
||||
if [[ -d "$STRIX_DIR" ]]; then
|
||||
emit "ok" "Directory exists: ${STRIX_DIR}"
|
||||
else
|
||||
emit "install" "Creating directory ${STRIX_DIR}"
|
||||
if mkdir -p "$STRIX_DIR" 2>/dev/null; then
|
||||
emit "ok" "Directory created: ${STRIX_DIR}"
|
||||
else
|
||||
emit "error" "Failed to create directory ${STRIX_DIR}"
|
||||
emit_done "false" "Cannot create ${STRIX_DIR}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Download docker-compose.yml
|
||||
# ---------------------------------------------------------------------------
|
||||
download_compose() {
|
||||
emit "check" "Checking docker-compose.yml"
|
||||
|
||||
if [[ -f "${STRIX_DIR}/docker-compose.yml" ]]; then
|
||||
emit "ok" "docker-compose.yml already exists"
|
||||
return
|
||||
fi
|
||||
|
||||
emit "install" "Downloading docker-compose.yml from GitHub"
|
||||
|
||||
if curl -fsSL "$COMPOSE_URL" -o "${STRIX_DIR}/docker-compose.yml" 2>/dev/null; then
|
||||
emit "ok" "docker-compose.yml downloaded"
|
||||
else
|
||||
emit "error" "Failed to download docker-compose.yml"
|
||||
emit_done "false" "docker-compose.yml download failed"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Generate .env
|
||||
# ---------------------------------------------------------------------------
|
||||
generate_env() {
|
||||
emit "check" "Generating .env configuration"
|
||||
|
||||
cat > "${STRIX_DIR}/.env" <<EOF
|
||||
# Strix configuration -- generated by strix.sh
|
||||
STRIX_LISTEN=:${STRIX_PORT}
|
||||
EOF
|
||||
|
||||
if [[ -n "$STRIX_FRIGATE_URL" ]]; then
|
||||
echo "STRIX_FRIGATE_URL=${STRIX_FRIGATE_URL}" >> "${STRIX_DIR}/.env"
|
||||
emit "ok" "Frigate URL: ${STRIX_FRIGATE_URL}" "{\"frigate_url\":\"${STRIX_FRIGATE_URL}\"}"
|
||||
fi
|
||||
|
||||
if [[ -n "$STRIX_GO2RTC_URL" ]]; then
|
||||
echo "STRIX_GO2RTC_URL=${STRIX_GO2RTC_URL}" >> "${STRIX_DIR}/.env"
|
||||
emit "ok" "go2rtc URL: ${STRIX_GO2RTC_URL}" "{\"go2rtc_url\":\"${STRIX_GO2RTC_URL}\"}"
|
||||
fi
|
||||
|
||||
if [[ -n "$STRIX_LOG_LEVEL" ]]; then
|
||||
echo "STRIX_LOG_LEVEL=${STRIX_LOG_LEVEL}" >> "${STRIX_DIR}/.env"
|
||||
emit "ok" "Log level: ${STRIX_LOG_LEVEL}"
|
||||
fi
|
||||
|
||||
emit "ok" ".env generated (port ${STRIX_PORT})" "{\"port\":\"${STRIX_PORT}\"}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Pull image
|
||||
# ---------------------------------------------------------------------------
|
||||
pull_image() {
|
||||
emit "check" "Pulling image ${IMAGE}:${STRIX_TAG}"
|
||||
|
||||
if docker compose -f "${STRIX_DIR}/docker-compose.yml" --env-file "${STRIX_DIR}/.env" pull &>/dev/null; then
|
||||
emit "ok" "Image pulled: ${IMAGE}:${STRIX_TAG}"
|
||||
else
|
||||
emit "error" "Failed to pull image ${IMAGE}:${STRIX_TAG}"
|
||||
emit_done "false" "Image pull failed"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Start container
|
||||
# ---------------------------------------------------------------------------
|
||||
start_container() {
|
||||
# Check if strix container is already running
|
||||
if docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^strix$'; then
|
||||
emit "check" "Strix container is running, recreating"
|
||||
if docker compose -f "${STRIX_DIR}/docker-compose.yml" --env-file "${STRIX_DIR}/.env" up -d --force-recreate &>/dev/null; then
|
||||
emit "ok" "Container recreated"
|
||||
else
|
||||
emit "error" "Failed to recreate container"
|
||||
emit_done "false" "Container recreate failed"
|
||||
fi
|
||||
else
|
||||
emit "install" "Starting Strix container"
|
||||
if docker compose -f "${STRIX_DIR}/docker-compose.yml" --env-file "${STRIX_DIR}/.env" up -d &>/dev/null; then
|
||||
emit "ok" "Container started"
|
||||
else
|
||||
emit "error" "Failed to start container"
|
||||
emit_done "false" "Container start failed"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Healthcheck
|
||||
# ---------------------------------------------------------------------------
|
||||
healthcheck() {
|
||||
emit "check" "Waiting for Strix to respond"
|
||||
|
||||
local retries=15
|
||||
local i
|
||||
for (( i = 1; i <= retries; i++ )); do
|
||||
if curl -sf --connect-timeout 2 --max-time 3 "http://localhost:${STRIX_PORT}/api/health" &>/dev/null; then
|
||||
local version
|
||||
version=$(curl -sf --max-time 3 "http://localhost:${STRIX_PORT}/api" 2>/dev/null | grep -oP '"version"\s*:\s*"\K[^"]+' || echo "unknown")
|
||||
emit "ok" "Strix v${version} is running on port ${STRIX_PORT}" "{\"version\":\"${version}\",\"port\":\"${STRIX_PORT}\"}"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
emit "error" "Healthcheck failed after ${retries} attempts"
|
||||
emit_done "false" "Healthcheck failed"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
main() {
|
||||
# 1. Working directory
|
||||
setup_dir
|
||||
|
||||
# 2. Download compose file (if not present)
|
||||
download_compose
|
||||
|
||||
# 3. Generate .env from parameters
|
||||
generate_env
|
||||
|
||||
# 4. Pull image
|
||||
pull_image
|
||||
|
||||
# 5. Start / recreate container
|
||||
start_container
|
||||
|
||||
# 6. Healthcheck
|
||||
healthcheck
|
||||
|
||||
# 7. Done -- include URL for navigator
|
||||
local lan_ip
|
||||
lan_ip=$(detect_lan_ip)
|
||||
local url="http://${lan_ip}:${STRIX_PORT}"
|
||||
|
||||
emit_done "true" "url" "$url" "version" "$(curl -sf --max-time 3 "http://localhost:${STRIX_PORT}/api" 2>/dev/null | grep -oP '"version"\s*:\s*"\K[^"]+' || echo "unknown")" "port" "$STRIX_PORT" "ip" "$lan_ip"
|
||||
}
|
||||
|
||||
main
|
||||
Reference in New Issue
Block a user