Publish deployment and backup scripts
This commit is contained in:
243
deploy-local-hermes.sh
Executable file
243
deploy-local-hermes.sh
Executable file
@@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# Hermes Agent Local (Bare-Metal) Deployment Script
|
||||
# ==============================================================================
|
||||
# Installs Hermes Agent directly on the host via uv + Python 3.11.
|
||||
# Runs as a gateway connecting to Telegram/Discord/Slack/WhatsApp.
|
||||
# No web UI — does NOT use Traefik.
|
||||
#
|
||||
# Source: https://github.com/NousResearch/hermes-agent
|
||||
#
|
||||
# Usage: deploy-local-hermes.sh
|
||||
# deploy-local-hermes.sh --remove [--purge] [--yes]
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/common.sh"
|
||||
|
||||
# --- Parse arguments ---
|
||||
parse_args "$@"
|
||||
|
||||
# --- Service config ---
|
||||
SERVICE_NAME="hermes"
|
||||
BASE_DIR="/opt/${SERVICE_NAME}"
|
||||
UNIT_NAME="${SERVICE_NAME}"
|
||||
HERMES_REPO="${HERMES_REPO:-https://github.com/NousResearch/hermes-agent.git}"
|
||||
|
||||
# --- Handle --remove ---
|
||||
if [[ "$ARG_REMOVE" == "1" ]]; then
|
||||
require_root
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}══════════════════════════════════════════════${NC}"
|
||||
echo -e "${YELLOW} Removing ${SERVICE_NAME} (local)${NC}"
|
||||
echo -e "${YELLOW}══════════════════════════════════════════════${NC}"
|
||||
if [[ "$ARG_PURGE" == "1" ]]; then
|
||||
echo -e " Mode: ${RED}Purge (delete all data)${NC}"
|
||||
else
|
||||
echo -e " Mode: Safe (keep data)"
|
||||
fi
|
||||
echo -e " Data: ${BASE_DIR}"
|
||||
echo -e " Systemd: ${UNIT_NAME}.service"
|
||||
echo -e "${YELLOW}══════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
if [[ "$ARG_PURGE" == "1" ]] && [[ -d "$BASE_DIR" ]]; then
|
||||
backup_script="${SCRIPT_DIR}/backup-${SERVICE_NAME}.sh"
|
||||
[[ -f "$backup_script" ]] && warn "Back up first? ${backup_script}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
unit_file="/etc/systemd/system/${UNIT_NAME}.service"
|
||||
if [[ -f "$unit_file" ]]; then
|
||||
systemctl stop "${UNIT_NAME}.service" > /dev/null 2>&1 || true
|
||||
systemctl disable "${UNIT_NAME}.service" > /dev/null 2>&1 || true
|
||||
rm -f "$unit_file"
|
||||
systemctl daemon-reload
|
||||
ok "Removed systemd unit: ${UNIT_NAME}.service"
|
||||
fi
|
||||
|
||||
if [[ "$ARG_PURGE" == "1" ]]; then
|
||||
echo ""
|
||||
warn "This will permanently delete: $BASE_DIR"
|
||||
if [[ "$ARG_YES" != "1" ]]; then
|
||||
read -rp "Are you sure? [y/N] " confirm
|
||||
if [[ "${confirm,,}" != "y" ]]; then
|
||||
info "Purge cancelled."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
rm -rf "$BASE_DIR"
|
||||
rm -rf "$HOME/.hermes" 2>/dev/null
|
||||
ok "Purged: $BASE_DIR"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
|
||||
echo -e "${GREEN} ${SERVICE_NAME} removed${NC}"
|
||||
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
|
||||
if [[ "$ARG_PURGE" != "1" ]]; then
|
||||
echo -e " Data preserved at ${BASE_DIR}"
|
||||
echo -e " To purge: $0 --remove --purge"
|
||||
fi
|
||||
echo -e " To redeploy: $0"
|
||||
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Detect current state for banner ---
|
||||
install_mode="Fresh install"
|
||||
if [[ -f "${BASE_DIR}/.env" ]]; then
|
||||
install_mode="Re-run (preserving data)"
|
||||
fi
|
||||
|
||||
service_status="Not running"
|
||||
if systemctl is-active --quiet "${UNIT_NAME}.service" 2>/dev/null; then
|
||||
service_status="Running"
|
||||
fi
|
||||
|
||||
# --- Print deployment plan ---
|
||||
echo ""
|
||||
echo -e "${CYAN}══════════════════════════════════════════════${NC}"
|
||||
echo -e "${CYAN} Deploying Hermes Agent (Local/Gateway)${NC}"
|
||||
echo -e "${CYAN}══════════════════════════════════════════════${NC}"
|
||||
echo -e " Data: ${BASE_DIR}/data/"
|
||||
echo -e " Traefik: Not used (no web UI)"
|
||||
echo -e " Service: ${service_status}"
|
||||
echo -e " Mode: ${install_mode}"
|
||||
echo -e "${CYAN}══════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
# --- Check if already deployed and running ---
|
||||
if [[ -f "${BASE_DIR}/.env" ]] && \
|
||||
systemctl is-active --quiet "${UNIT_NAME}.service" 2>/dev/null; then
|
||||
echo -e "${GREEN} Hermes is already running — nothing to do.${NC}"
|
||||
echo ""
|
||||
echo -e " Health: hermes doctor"
|
||||
echo -e " Logs: journalctl -u ${UNIT_NAME}.service -f"
|
||||
echo -e " Backup: ${SCRIPT_DIR}/backup-hermes.sh"
|
||||
echo -e " Remove: $0 --remove [--purge]"
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# --- Infrastructure ---
|
||||
require_root
|
||||
detect_os
|
||||
|
||||
# --- Install prerequisites ---
|
||||
info "Installing prerequisites..."
|
||||
case "$DISTRO_FAMILY" in
|
||||
debian)
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq curl git openssl ca-certificates \
|
||||
python3 python3-venv python3-pip > /dev/null 2>&1
|
||||
;;
|
||||
fedora)
|
||||
dnf install -y -q curl git openssl ca-certificates \
|
||||
python3 python3-pip > /dev/null 2>&1
|
||||
;;
|
||||
esac
|
||||
ok "System prerequisites installed."
|
||||
|
||||
# Install uv (Python package manager)
|
||||
if ! command -v uv &>/dev/null; then
|
||||
info "Installing uv..."
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh > /dev/null 2>&1
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
fi
|
||||
ok "uv $(uv --version 2>/dev/null || echo 'installed')."
|
||||
|
||||
# --- Backup restore check ---
|
||||
if check_and_restore_backup "$SERVICE_NAME"; then
|
||||
ok "Backup restored."
|
||||
else
|
||||
# --- Fresh install ---
|
||||
if [[ -d "${BASE_DIR}/.git" ]]; then
|
||||
info "Updating Hermes source..."
|
||||
(cd "$BASE_DIR" && git pull)
|
||||
else
|
||||
info "Cloning Hermes from ${HERMES_REPO}..."
|
||||
git clone --recurse-submodules "$HERMES_REPO" "$BASE_DIR"
|
||||
fi
|
||||
|
||||
info "Creating Python venv and installing dependencies..."
|
||||
(cd "$BASE_DIR" && uv venv venv --python 3.11 2>/dev/null || uv venv venv)
|
||||
export VIRTUAL_ENV="${BASE_DIR}/venv"
|
||||
(cd "$BASE_DIR" && uv pip install -e ".[all]" 2>/dev/null) || \
|
||||
(cd "$BASE_DIR" && "${BASE_DIR}/venv/bin/pip" install -e ".[all]")
|
||||
ok "Hermes installed."
|
||||
|
||||
# Symlink binary
|
||||
mkdir -p /usr/local/bin
|
||||
ln -sf "${BASE_DIR}/venv/bin/hermes" /usr/local/bin/hermes
|
||||
|
||||
# Data directory — redirect ~/.hermes here
|
||||
mkdir -p "${BASE_DIR}/data"
|
||||
rm -rf "$HOME/.hermes" 2>/dev/null
|
||||
ln -sfn "${BASE_DIR}/data" "$HOME/.hermes"
|
||||
|
||||
# Create data subdirs if fresh
|
||||
if [[ ! -d "${BASE_DIR}/data/sessions" ]]; then
|
||||
mkdir -p "${BASE_DIR}/data"/{cron,sessions,logs,memories,skills,pairing,hooks,image_cache,audio_cache}
|
||||
fi
|
||||
|
||||
if [[ ! -f "${BASE_DIR}/.env" ]]; then
|
||||
cat > "${BASE_DIR}/.env" <<DOTENV
|
||||
# Generated by deploy-local-hermes.sh on $(date -Iseconds)
|
||||
DOTENV
|
||||
chmod 600 "${BASE_DIR}/.env"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Install systemd unit ---
|
||||
cat > "/etc/systemd/system/${UNIT_NAME}.service" <<EOF
|
||||
[Unit]
|
||||
Description=Hermes Agent Gateway
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=${BASE_DIR}
|
||||
Environment=VIRTUAL_ENV=${BASE_DIR}/venv
|
||||
Environment=PATH=${BASE_DIR}/venv/bin:/usr/local/bin:/usr/bin:/bin
|
||||
ExecStart=${BASE_DIR}/venv/bin/hermes gateway run
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
TimeoutStartSec=60
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable "${UNIT_NAME}.service" > /dev/null 2>&1
|
||||
systemctl restart "${UNIT_NAME}.service"
|
||||
ok "Systemd unit installed and started: ${UNIT_NAME}.service"
|
||||
|
||||
# --- Health check ---
|
||||
sleep 3
|
||||
if hermes doctor > /dev/null 2>&1; then
|
||||
ok "Hermes is healthy."
|
||||
else
|
||||
warn "hermes doctor failed — may need initial setup (API keys)."
|
||||
fi
|
||||
|
||||
# --- Summary ---
|
||||
echo ""
|
||||
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
|
||||
echo -e "${GREEN} Hermes Agent deployed successfully (local)${NC}"
|
||||
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
|
||||
echo -e " Data: ${BASE_DIR}/data/"
|
||||
echo -e " Systemd: systemctl status ${UNIT_NAME}"
|
||||
echo -e " Logs: journalctl -u ${UNIT_NAME}.service -f"
|
||||
echo ""
|
||||
echo -e " ${YELLOW}First run? Set up API keys:${NC}"
|
||||
echo -e " hermes setup"
|
||||
echo -e " Then: systemctl restart ${UNIT_NAME}"
|
||||
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user