Add main entry point + systemd services + integration tests
New files:
main.py - PedalApp: boots all subsystems in order,
wires MIDI/footswitch callbacks, graceful
teardown reverses boot order
src/system/config.py - YAML config loader with deep-merge
(separated to avoid hardware deps)
src/system/services.py - systemd unit generator for pedal.service
+ multi-fx-pedal.target
scripts/install_service.sh - copies project, creates venv, installs
+ enables service units
tests/test_integration.py - 41 tests: boot, routing, display sync,
teardown, systemd content, CLI, edge cases
Modified:
tests/conftest.py - add project root to sys.path
This commit is contained in:
Executable
+166
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env bash
|
||||
# ────────────────────────────────────────────────────────────────────
|
||||
# Pi Multi-FX Pedal — systemd service installer
|
||||
#
|
||||
# Installs pi-multifx-pedal.service + multi-fx-pedal.target and
|
||||
# enables them for auto-start on boot.
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./install_service.sh [install_dir]
|
||||
#
|
||||
# install_dir Path to the pedal installation (default: /opt/pi-multifx-pedal)
|
||||
# ────────────────────────────────────────────────────────────────────
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL_DIR="${1:-/opt/pi-multifx-pedal}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# ── Colour helpers ─────────────────────────────────────────────────
|
||||
info() { printf "\e[34m[INFO] %s\e[0m\n" "$*"; }
|
||||
ok() { printf "\e[32m[ OK ] %s\e[0m\n" "$*"; }
|
||||
warn() { printf "\e[33m[WARN] %s\e[0m\n" "$*"; }
|
||||
err() { printf "\e[31m[FAIL] %s\e[0m\n" "$*"; }
|
||||
|
||||
# ── Root check ─────────────────────────────────────────────────────
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
err "This script must be run as root (sudo ./install_service.sh)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Architecture check (must be RPi) ───────────────────────────────
|
||||
ARCH="$(uname -m)"
|
||||
if [[ "$ARCH" != "aarch64" && "$ARCH" != "armv7l" ]]; then
|
||||
warn "Not a Raspberry Pi (detected: $ARCH) — service files will be written"
|
||||
warn "but will not be enabled (no systemctl available in containers)."
|
||||
NO_SYSTEMCTL=true
|
||||
fi
|
||||
|
||||
info "========== Pi Multi-FX Pedal — Service Installer =========="
|
||||
info "Install dir: $INSTALL_DIR"
|
||||
info "Project dir: $PROJECT_DIR"
|
||||
echo ""
|
||||
|
||||
# ── Step 1: Create install directory ───────────────────────────────
|
||||
info "Step 1: Creating install directory..."
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
ok "Install directory ready: $INSTALL_DIR"
|
||||
|
||||
# ── Step 2: Copy project files ─────────────────────────────────────
|
||||
info "Step 2: Copying project files..."
|
||||
|
||||
# Copy everything except .venv, __pycache__, .git, etc.
|
||||
rsync -a --delete \
|
||||
--exclude='.git' \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='*.pyc' \
|
||||
--exclude='.venv' \
|
||||
--exclude='venv' \
|
||||
--exclude='.scone' \
|
||||
--exclude='systemd/' \
|
||||
--exclude='*.so' \
|
||||
--exclude='*.nam' \
|
||||
--exclude='*.wav' \
|
||||
--exclude='*.aiff' \
|
||||
"$PROJECT_DIR/" "$INSTALL_DIR/"
|
||||
|
||||
ok "Project files copied to $INSTALL_DIR"
|
||||
|
||||
# ── Step 3: Create Python virtual environment ──────────────────────
|
||||
info "Step 3: Setting up Python virtual environment..."
|
||||
|
||||
if [[ ! -d "$INSTALL_DIR/.venv" ]]; then
|
||||
python3 -m venv "$INSTALL_DIR/.venv"
|
||||
ok "Virtual environment created at $INSTALL_DIR/.venv"
|
||||
else
|
||||
info "Virtual environment already exists"
|
||||
fi
|
||||
|
||||
# Install/update dependencies
|
||||
"$INSTALL_DIR/.venv/bin/pip" install --quiet --upgrade pip
|
||||
"$INSTALL_DIR/.venv/bin/pip" install --quiet -r "$INSTALL_DIR/requirements.txt" 2>&1 | tail -1
|
||||
ok "Python dependencies installed"
|
||||
|
||||
# ── Step 4: Generate service files from Python module ──────────────
|
||||
info "Step 4: Generating systemd service units..."
|
||||
|
||||
PYTHON_BIN="$INSTALL_DIR/.venv/bin/python3"
|
||||
SERVICES_MODULE="src.system.services"
|
||||
|
||||
# Generate pi-multifx-pedal.service
|
||||
$PYTHON_BIN -c "
|
||||
import sys
|
||||
sys.path.insert(0, '$INSTALL_DIR')
|
||||
from src.system.services import pedal_service_content
|
||||
print(pedal_service_content(install_dir='$INSTALL_DIR'))
|
||||
" > /etc/systemd/system/pi-multifx-pedal.service
|
||||
ok "Generated /etc/systemd/system/pi-multifx-pedal.service"
|
||||
|
||||
# Generate multi-fx-pedal.target
|
||||
$PYTHON_BIN -c "
|
||||
import sys
|
||||
sys.path.insert(0, '$INSTALL_DIR')
|
||||
from src.system.services import pedal_target_content
|
||||
print(pedal_target_content())
|
||||
" > /etc/systemd/system/multi-fx-pedal.target
|
||||
ok "Generated /etc/systemd/system/multi-fx-pedal.target"
|
||||
|
||||
# ── Step 5: Reload and enable ──────────────────────────────────────
|
||||
info "Step 5: Reloading systemd and enabling services..."
|
||||
|
||||
if [[ "${NO_SYSTEMCTL:-false}" != true ]]; then
|
||||
systemctl daemon-reload
|
||||
systemctl enable pi-multifx-pedal.service
|
||||
systemctl enable multi-fx-pedal.target
|
||||
ok "Services enabled: pi-multifx-pedal.service + multi-fx-pedal.target"
|
||||
else
|
||||
info "Skipping systemctl enable (non-RPi environment)"
|
||||
fi
|
||||
|
||||
# ── Step 6: Add 'pi' user to audio group ───────────────────────────
|
||||
if groups pi | grep -q '\baudio\b' 2>/dev/null; then
|
||||
ok "User 'pi' already in audio group"
|
||||
else
|
||||
usermod -a -G audio pi 2>/dev/null || true
|
||||
info "Added 'pi' to audio group (will take effect on next login)"
|
||||
fi
|
||||
|
||||
# ── Optional: Set up log rotation ──────────────────────────────────
|
||||
LOGROTATE_FILE="/etc/logrotate.d/pi-multifx-pedal"
|
||||
if [[ ! -f "$LOGROTATE_FILE" ]]; then
|
||||
cat > "$LOGROTATE_FILE" <<'LOGROTATE'
|
||||
/var/log/pi-multifx-pedal/*.log {
|
||||
daily
|
||||
rotate 7
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
copytruncate
|
||||
}
|
||||
LOGROTATE
|
||||
ok "Log rotation configured at $LOGROTATE_FILE"
|
||||
fi
|
||||
|
||||
# ── Summary ────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
info "========== Installation Complete =========="
|
||||
echo ""
|
||||
echo " Service: pi-multifx-pedal.service"
|
||||
echo " Target: multi-fx-pedal.target"
|
||||
echo " Install: $INSTALL_DIR"
|
||||
echo " Config: ~/.pedal/config.yaml"
|
||||
echo " Presets: ~/.pedal/presets/"
|
||||
echo ""
|
||||
info "Start now:"
|
||||
echo " sudo systemctl start multi-fx-pedal.target"
|
||||
echo ""
|
||||
info "Check status:"
|
||||
echo " sudo systemctl status pi-multifx-pedal.service"
|
||||
echo ""
|
||||
info "Tail logs:"
|
||||
echo " journalctl -fu pi-multifx-pedal.service"
|
||||
echo ""
|
||||
info "Stop the pedal:"
|
||||
echo " sudo systemctl stop multi-fx-pedal.target"
|
||||
Reference in New Issue
Block a user