Add git-based auto-update for Linux deployment
- install.sh now clones from git instead of copying files - Service runs git pull on restart for automatic updates - Support config.local.json for production settings (gitignored) - kao.py prefers config.local.json when present To update production: push changes, then 'sudo systemctl restart kao' Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,7 @@ __pycache__/
|
|||||||
|
|
||||||
# Runtime files
|
# Runtime files
|
||||||
status.json
|
status.json
|
||||||
|
config.local.json
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -2,12 +2,17 @@
|
|||||||
|
|
||||||
A minimalist system status monitor that uses ASCII emotes to display server health on an old phone.
|
A minimalist system status monitor that uses ASCII emotes to display server health on an old phone.
|
||||||
|
|
||||||
-brightgreen)
|
-brightgreen>)
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
|
|
||||||
Turn an old phone (with its OLED screen) into a glanceable ambient display for your home server. Instead of graphs and numbers, see a happy face `( ^_^)` when things are good, and a worried face `( o_o)` when they're not.
|
Turn an old phone (with its OLED screen) into a glanceable ambient display for your home server. Instead of graphs and numbers, see a happy face `( ^_^)` when things are good, and a worried face `( o_o)` when they're not.
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Figure out way to update installation in /opt/kao when git updates. Maybe run a git pull on service startup?
|
||||||
|
- Think about ways of implementing noises.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **OLED-optimized** — Pure black background, saves battery
|
- **OLED-optimized** — Pure black background, saves battery
|
||||||
@@ -35,7 +40,7 @@ Open http://localhost:5100 on your phone (use Fully Kiosk Browser for best resul
|
|||||||
## Status Faces
|
## Status Faces
|
||||||
|
|
||||||
| State | Emote | Meaning |
|
| State | Emote | Meaning |
|
||||||
|-------|-------|---------|
|
| ------------ | ----------- | ------------------------- |
|
||||||
| Optimal | `( ^_^)` | All systems healthy |
|
| Optimal | `( ^_^)` | All systems healthy |
|
||||||
| Warning | `( o_o)` | Something needs attention |
|
| Warning | `( o_o)` | Something needs attention |
|
||||||
| Critical | `( x_x)` | Immediate action required |
|
| Critical | `( x_x)` | Immediate action required |
|
||||||
@@ -46,7 +51,7 @@ Open http://localhost:5100 on your phone (use Fully Kiosk Browser for best resul
|
|||||||
## Built-in Detectors
|
## Built-in Detectors
|
||||||
|
|
||||||
| Detector | Monitors |
|
| Detector | Monitors |
|
||||||
|----------|----------|
|
| -------------- | ----------------------------- |
|
||||||
| **disk_space** | Disk usage on all drives |
|
| **disk_space** | Disk usage on all drives |
|
||||||
| **cpu** | CPU utilization |
|
| **cpu** | CPU utilization |
|
||||||
| **memory** | RAM usage |
|
| **memory** | RAM usage |
|
||||||
@@ -91,6 +96,7 @@ curl -X POST http://localhost:5100/event \
|
|||||||
- `ttl` — Auto-expire after N seconds (for heartbeat pattern)
|
- `ttl` — Auto-expire after N seconds (for heartbeat pattern)
|
||||||
|
|
||||||
Clear an event:
|
Clear an event:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://localhost:5100/clear \
|
curl -X POST http://localhost:5100/clear \
|
||||||
-d '{"id": "my_check"}'
|
-d '{"id": "my_check"}'
|
||||||
@@ -132,7 +138,7 @@ automation:
|
|||||||
## API Reference
|
## API Reference
|
||||||
|
|
||||||
| Endpoint | Method | Description |
|
| Endpoint | Method | Description |
|
||||||
|----------|--------|-------------|
|
| --------- | ------ | ---------------------- |
|
||||||
| `/` | GET | Web UI |
|
| `/` | GET | Web UI |
|
||||||
| `/status` | GET | Current state as JSON |
|
| `/status` | GET | Current state as JSON |
|
||||||
| `/events` | GET | List all active events |
|
| `/events` | GET | List all active events |
|
||||||
|
|||||||
74
install.sh
74
install.sh
@@ -1,6 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Kao Installation Script
|
# Kao Installation Script
|
||||||
# Installs to /opt/kao and sets up systemd service
|
# Clones from git to /opt/kao and sets up systemd service
|
||||||
|
# Updates automatically via git pull on service restart
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -17,7 +18,6 @@ echo " Kao Installer"
|
|||||||
echo "===================================="
|
echo "===================================="
|
||||||
echo "Install directory: $INSTALL_DIR"
|
echo "Install directory: $INSTALL_DIR"
|
||||||
echo "Running as user: $CURRENT_USER"
|
echo "Running as user: $CURRENT_USER"
|
||||||
echo "Source: $SCRIPT_DIR"
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Check if running as root
|
# Check if running as root
|
||||||
@@ -40,34 +40,58 @@ if ! command -v python3 &> /dev/null; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check source files exist
|
# Check for git
|
||||||
if [ ! -f "$SCRIPT_DIR/kao.py" ]; then
|
if ! command -v git &> /dev/null; then
|
||||||
echo "Error: Source files not found in $SCRIPT_DIR"
|
echo "Error: git not found"
|
||||||
echo "Run this script from the Kao repository directory"
|
echo "Install with: sudo apt install git"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create install directory
|
# Get git remote URL from source repo
|
||||||
echo "[1/5] Creating install directory..."
|
if [ -d "$SCRIPT_DIR/.git" ]; then
|
||||||
mkdir -p "$INSTALL_DIR"
|
REPO_URL=$(git -C "$SCRIPT_DIR" remote get-url origin 2>/dev/null || echo "")
|
||||||
cp "$SCRIPT_DIR/aggregator.py" "$INSTALL_DIR/"
|
fi
|
||||||
cp "$SCRIPT_DIR/kao.py" "$INSTALL_DIR/"
|
|
||||||
cp "$SCRIPT_DIR/index.html" "$INSTALL_DIR/"
|
|
||||||
cp "$SCRIPT_DIR/config.json" "$INSTALL_DIR/"
|
|
||||||
cp "$SCRIPT_DIR/requirements.txt" "$INSTALL_DIR/"
|
|
||||||
cp -r "$SCRIPT_DIR/detectors" "$INSTALL_DIR/"
|
|
||||||
|
|
||||||
# Set ownership before venv creation
|
if [ -z "$REPO_URL" ]; then
|
||||||
|
echo "Error: Could not detect git remote URL"
|
||||||
|
echo "Run this script from within the Kao git repository"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Git remote: $REPO_URL"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Remove existing installation if present
|
||||||
|
if [ -d "$INSTALL_DIR" ]; then
|
||||||
|
echo "[1/6] Removing existing installation..."
|
||||||
|
systemctl stop kao.service 2>/dev/null || true
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
else
|
||||||
|
echo "[1/6] No existing installation found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clone repository (as root since /opt requires elevated permissions)
|
||||||
|
echo "[2/6] Cloning repository..."
|
||||||
|
git clone "$REPO_URL" "$INSTALL_DIR"
|
||||||
|
|
||||||
|
# Set ownership to target user (so they can git pull, create venv, etc.)
|
||||||
chown -R "$CURRENT_USER:$CURRENT_GROUP" "$INSTALL_DIR"
|
chown -R "$CURRENT_USER:$CURRENT_GROUP" "$INSTALL_DIR"
|
||||||
|
|
||||||
|
# Create local config for production customization
|
||||||
|
echo "[3/6] Creating local config..."
|
||||||
|
if [ ! -f "$INSTALL_DIR/config.local.json" ]; then
|
||||||
|
cp "$INSTALL_DIR/config.json" "$INSTALL_DIR/config.local.json"
|
||||||
|
echo "Created config.local.json - edit this for production settings"
|
||||||
|
fi
|
||||||
|
|
||||||
# Create virtual environment as target user
|
# Create virtual environment as target user
|
||||||
echo "[2/5] Creating virtual environment..."
|
echo "[4/6] Creating virtual environment..."
|
||||||
sudo -u "$CURRENT_USER" python3 -m venv "$INSTALL_DIR/venv"
|
sudo -u "$CURRENT_USER" python3 -m venv "$INSTALL_DIR/venv"
|
||||||
sudo -u "$CURRENT_USER" "$INSTALL_DIR/venv/bin/pip" install --upgrade pip -q
|
sudo -u "$CURRENT_USER" "$INSTALL_DIR/venv/bin/pip" install --upgrade pip -q
|
||||||
sudo -u "$CURRENT_USER" "$INSTALL_DIR/venv/bin/pip" install -r "$INSTALL_DIR/requirements.txt" -q
|
sudo -u "$CURRENT_USER" "$INSTALL_DIR/venv/bin/pip" install -r "$INSTALL_DIR/requirements.txt" -q
|
||||||
|
|
||||||
# Create systemd service
|
# Create systemd service with git pull on start
|
||||||
echo "[3/5] Creating systemd service..."
|
echo "[5/6] Creating systemd service..."
|
||||||
cat > "$SERVICE_FILE" << EOF
|
cat > "$SERVICE_FILE" << EOF
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Kao - System Status Monitor
|
Description=Kao - System Status Monitor
|
||||||
@@ -78,6 +102,7 @@ Type=simple
|
|||||||
User=$CURRENT_USER
|
User=$CURRENT_USER
|
||||||
Group=$CURRENT_GROUP
|
Group=$CURRENT_GROUP
|
||||||
WorkingDirectory=$INSTALL_DIR
|
WorkingDirectory=$INSTALL_DIR
|
||||||
|
ExecStartPre=/usr/bin/git -C $INSTALL_DIR pull --ff-only
|
||||||
ExecStart=$INSTALL_DIR/venv/bin/python $INSTALL_DIR/kao.py
|
ExecStart=$INSTALL_DIR/venv/bin/python $INSTALL_DIR/kao.py
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
@@ -87,12 +112,9 @@ WantedBy=multi-user.target
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Reload systemd and enable service
|
# Reload systemd and enable service
|
||||||
echo "[4/5] Enabling service..."
|
echo "[6/6] Enabling and starting service..."
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable kao.service
|
systemctl enable kao.service
|
||||||
|
|
||||||
# Start service
|
|
||||||
echo "[5/5] Starting Kao..."
|
|
||||||
systemctl start kao.service
|
systemctl start kao.service
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -104,8 +126,10 @@ echo "Kao is now running at http://$(hostname -I | awk '{print $1}'):5100"
|
|||||||
echo ""
|
echo ""
|
||||||
echo "Commands:"
|
echo "Commands:"
|
||||||
echo " sudo systemctl status kao # Check status"
|
echo " sudo systemctl status kao # Check status"
|
||||||
echo " sudo systemctl restart kao # Restart"
|
echo " sudo systemctl restart kao # Restart (auto-pulls latest code)"
|
||||||
echo " sudo systemctl stop kao # Stop"
|
echo " sudo systemctl stop kao # Stop"
|
||||||
echo " sudo journalctl -u kao -f # View logs"
|
echo " sudo journalctl -u kao -f # View logs"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Config file: $INSTALL_DIR/config.json"
|
echo "Config: $INSTALL_DIR/config.local.json"
|
||||||
|
echo "To update: push changes to git, then 'sudo systemctl restart kao'"
|
||||||
|
echo ""
|
||||||
|
|||||||
12
kao.py
12
kao.py
@@ -242,10 +242,18 @@ def main():
|
|||||||
else:
|
else:
|
||||||
config_path = sys.argv[1]
|
config_path = sys.argv[1]
|
||||||
|
|
||||||
# Resolve config path
|
# Resolve config path - prefer config.local.json if it exists
|
||||||
|
base_dir = Path(__file__).parent
|
||||||
config_path = Path(config_path)
|
config_path = Path(config_path)
|
||||||
|
|
||||||
if not config_path.is_absolute():
|
if not config_path.is_absolute():
|
||||||
config_path = Path(__file__).parent / config_path
|
# Check for local config override first
|
||||||
|
local_config = base_dir / "config.local.json"
|
||||||
|
if local_config.exists() and config_path.name == "config.json":
|
||||||
|
config_path = local_config
|
||||||
|
print(f"Using local config: {config_path}")
|
||||||
|
else:
|
||||||
|
config_path = base_dir / config_path
|
||||||
|
|
||||||
if not config_path.exists():
|
if not config_path.exists():
|
||||||
print(f"Config file not found: {config_path}")
|
print(f"Config file not found: {config_path}")
|
||||||
|
|||||||
Reference in New Issue
Block a user