Skip to content

Commit f01f03c

Browse files
DavidsonGomesclaude
andcommitted
feat: add install-service.sh — systemd setup with dedicated user
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 64f82d8 commit f01f03c

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

install-service.sh

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#!/bin/bash
2+
# EvoNexus — Install as systemd service with dedicated user
3+
# Usage: sudo bash install-service.sh [install_dir]
4+
#
5+
# Creates an 'evonexus' system user, copies/chowns the installation,
6+
# installs uv + claude-code for that user, and sets up a systemd service.
7+
# Safe to re-run — skips steps that are already done.
8+
9+
set -euo pipefail
10+
11+
GREEN='\033[0;32m'
12+
YELLOW='\033[0;33m'
13+
RED='\033[0;31m'
14+
DIM='\033[0;90m'
15+
RESET='\033[0m'
16+
17+
# ── Preflight ──
18+
19+
if [ "$(id -u)" -ne 0 ]; then
20+
echo -e "${RED}✗ Must run as root (sudo bash install-service.sh)${RESET}"
21+
exit 1
22+
fi
23+
24+
INSTALL_DIR="${1:-$(pwd)}"
25+
if [ ! -f "$INSTALL_DIR/pyproject.toml" ]; then
26+
echo -e "${RED}✗ Not an EvoNexus installation: $INSTALL_DIR${RESET}"
27+
echo " Run from the evo-nexus directory, or pass the path: sudo bash install-service.sh /path/to/evo-nexus"
28+
exit 1
29+
fi
30+
31+
SERVICE_USER="evonexus"
32+
SERVICE_HOME="/home/$SERVICE_USER"
33+
SERVICE_DIR="$SERVICE_HOME/evo-nexus"
34+
SERVICE_NAME="evo-nexus"
35+
36+
echo -e "\n${GREEN}EvoNexus — Service Installer${RESET}\n"
37+
38+
# ── Step 1: Create user ──
39+
40+
if id "$SERVICE_USER" &>/dev/null; then
41+
echo -e " ${DIM}✓ User '$SERVICE_USER' already exists${RESET}"
42+
else
43+
echo -e " Creating user '$SERVICE_USER'..."
44+
useradd -m -s /bin/bash "$SERVICE_USER"
45+
echo -e " ${GREEN}${RESET} User '$SERVICE_USER' created"
46+
fi
47+
48+
# ── Step 2: Copy installation to user home (if not already there) ──
49+
50+
INSTALL_DIR_REAL=$(realpath "$INSTALL_DIR")
51+
SERVICE_DIR_REAL=$(realpath "$SERVICE_DIR" 2>/dev/null || echo "$SERVICE_DIR")
52+
53+
if [ "$INSTALL_DIR_REAL" = "$SERVICE_DIR_REAL" ]; then
54+
echo -e " ${DIM}✓ Already installed at $SERVICE_DIR${RESET}"
55+
else
56+
echo -e " Copying installation to $SERVICE_DIR..."
57+
# Remove old copy if exists
58+
rm -rf "$SERVICE_DIR"
59+
cp -a "$INSTALL_DIR_REAL" "$SERVICE_DIR"
60+
echo -e " ${GREEN}${RESET} Copied to $SERVICE_DIR"
61+
fi
62+
63+
chown -R "$SERVICE_USER:$SERVICE_USER" "$SERVICE_DIR"
64+
chown -R "$SERVICE_USER:$SERVICE_USER" "$SERVICE_HOME"
65+
66+
# ── Step 3: Install uv for the user ──
67+
68+
if su - "$SERVICE_USER" -c "command -v uv" &>/dev/null; then
69+
echo -e " ${DIM}✓ uv already installed${RESET}"
70+
else
71+
echo -e " Installing uv..."
72+
su - "$SERVICE_USER" -c "curl -LsSf https://astral.sh/uv/install.sh | sh" >/dev/null 2>&1
73+
echo -e " ${GREEN}${RESET} uv installed"
74+
fi
75+
76+
# ── Step 4: Install Claude Code for the user ──
77+
78+
if su - "$SERVICE_USER" -c "export PATH=\$HOME/.local/bin:\$PATH && command -v claude" &>/dev/null; then
79+
echo -e " ${DIM}✓ Claude Code already installed${RESET}"
80+
else
81+
echo -e " Installing Claude Code..."
82+
su - "$SERVICE_USER" -c "npm install -g @anthropic-ai/claude-code --prefix ~/.local" >/dev/null 2>&1
83+
echo -e " ${GREEN}${RESET} Claude Code installed"
84+
fi
85+
86+
# ── Step 5: Sync Python dependencies ──
87+
88+
echo -e " Syncing Python dependencies..."
89+
su - "$SERVICE_USER" -c "export PATH=\$HOME/.local/bin:\$PATH && cd $SERVICE_DIR && uv sync -q" 2>/dev/null
90+
echo -e " ${GREEN}${RESET} Dependencies synced"
91+
92+
# ── Step 6: Create systemd service ──
93+
94+
echo -e " Creating systemd service..."
95+
96+
cat > /etc/systemd/system/${SERVICE_NAME}.service << SERVICEEOF
97+
[Unit]
98+
Description=EvoNexus Dashboard + Scheduler + Terminal Server
99+
After=network.target
100+
Documentation=https://github.com/EvolutionAPI/evo-nexus
101+
102+
[Service]
103+
Type=forking
104+
User=$SERVICE_USER
105+
Group=$SERVICE_USER
106+
WorkingDirectory=$SERVICE_DIR
107+
Environment=PATH=$SERVICE_HOME/.local/bin:/usr/local/bin:/usr/bin:/bin
108+
Environment=HOME=$SERVICE_HOME
109+
ExecStart=/bin/bash $SERVICE_DIR/start-services.sh
110+
ExecStop=/bin/bash -c 'pkill -f "terminal-server/bin/server.js" 2>/dev/null; pkill -f "dashboard/backend.*app.py" 2>/dev/null'
111+
PIDFile=$SERVICE_DIR/logs/dashboard.pid
112+
Restart=on-failure
113+
RestartSec=10
114+
StandardOutput=append:$SERVICE_DIR/logs/service.log
115+
StandardError=append:$SERVICE_DIR/logs/service.log
116+
117+
[Install]
118+
WantedBy=multi-user.target
119+
SERVICEEOF
120+
121+
# Ensure logs dir exists
122+
su - "$SERVICE_USER" -c "mkdir -p $SERVICE_DIR/logs"
123+
124+
systemctl daemon-reload
125+
systemctl enable "$SERVICE_NAME" >/dev/null 2>&1
126+
127+
echo -e " ${GREEN}${RESET} Systemd service created and enabled"
128+
129+
# ── Step 7: Stop old services running as root ──
130+
131+
echo -e " Stopping old root services..."
132+
pkill -f 'terminal-server/bin/server.js' 2>/dev/null || true
133+
pkill -f 'dashboard/backend.*app.py' 2>/dev/null || true
134+
sleep 1
135+
136+
# ── Step 8: Start the service ──
137+
138+
echo -e " Starting $SERVICE_NAME service..."
139+
systemctl start "$SERVICE_NAME"
140+
sleep 3
141+
142+
# Verify
143+
if systemctl is-active --quiet "$SERVICE_NAME"; then
144+
echo -e " ${GREEN}${RESET} Service is running"
145+
else
146+
echo -e " ${YELLOW}!${RESET} Service may not have started — check: journalctl -u $SERVICE_NAME -n 30"
147+
fi
148+
149+
# ── Done ──
150+
151+
echo -e "\n${GREEN}Done!${RESET} EvoNexus is running as '$SERVICE_USER' via systemd.\n"
152+
echo -e " Useful commands:"
153+
echo -e " ${DIM}systemctl status $SERVICE_NAME${RESET} — check status"
154+
echo -e " ${DIM}systemctl restart $SERVICE_NAME${RESET} — restart"
155+
echo -e " ${DIM}journalctl -u $SERVICE_NAME -f${RESET} — follow logs"
156+
echo -e " ${DIM}su - $SERVICE_USER${RESET} — switch to service user"
157+
echo -e " ${DIM}su - $SERVICE_USER -c 'cd ~/evo-nexus && make run R=morning'${RESET} — run routine manually"
158+
echo ""

0 commit comments

Comments
 (0)