220 lines
8.3 KiB
Bash
220 lines
8.3 KiB
Bash
#!/usr/bin/env bash
|
|
# ─────────────────────────────────────────────────────────────
|
|
# LLM Trainer — .deb package builder
|
|
# Run this on the Ubuntu machine after cloning the repo:
|
|
# chmod +x packaging/build-deb.sh
|
|
# ./packaging/build-deb.sh
|
|
# ─────────────────────────────────────────────────────────────
|
|
set -euo pipefail
|
|
|
|
PKG_NAME="llm-trainer"
|
|
PKG_VERSION="1.0.0"
|
|
PKG_ARCH="amd64"
|
|
PKG_DIR="${PKG_NAME}_${PKG_VERSION}_${PKG_ARCH}"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
OUT_DIR="${REPO_ROOT}"
|
|
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo " LLM Trainer .deb Builder"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# ── Check dependencies ────────────────────────────────────────
|
|
for cmd in node npm python3 pip3 dpkg-deb nginx; do
|
|
if ! command -v "$cmd" &>/dev/null; then
|
|
echo "[ERROR] Required command not found: $cmd"
|
|
echo " Install with: sudo apt install nodejs npm python3-pip nginx"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# ── Build frontend ────────────────────────────────────────────
|
|
echo ""
|
|
echo "[1/4] Building React frontend..."
|
|
cd "${REPO_ROOT}/frontend"
|
|
npm install --silent
|
|
npm run build
|
|
echo " Frontend built → frontend/dist"
|
|
|
|
# ── Create package directory tree ─────────────────────────────
|
|
echo ""
|
|
echo "[2/4] Creating package structure..."
|
|
BUILD="${REPO_ROOT}/${PKG_DIR}"
|
|
rm -rf "${BUILD}"
|
|
|
|
install -d "${BUILD}/DEBIAN"
|
|
install -d "${BUILD}/opt/llm-trainer/backend"
|
|
install -d "${BUILD}/opt/llm-trainer/remote"
|
|
install -d "${BUILD}/opt/llm-trainer/frontend"
|
|
install -d "${BUILD}/opt/llm-trainer/venv"
|
|
install -d "${BUILD}/lib/systemd/system"
|
|
install -d "${BUILD}/etc/nginx/sites-available"
|
|
install -d "${BUILD}/etc/nginx/sites-enabled"
|
|
install -d "${BUILD}/etc/llm-trainer"
|
|
install -d "${BUILD}/var/log/llm-trainer"
|
|
|
|
# Copy backend
|
|
cp "${REPO_ROOT}/backend/"*.py "${BUILD}/opt/llm-trainer/backend/"
|
|
cp "${REPO_ROOT}/backend/requirements.txt" "${BUILD}/opt/llm-trainer/backend/"
|
|
|
|
# Copy remote bootstrap assets
|
|
cp "${REPO_ROOT}/packaging/remote/"* "${BUILD}/opt/llm-trainer/remote/"
|
|
|
|
# Copy built frontend
|
|
cp -r "${REPO_ROOT}/frontend/dist/." "${BUILD}/opt/llm-trainer/frontend/"
|
|
|
|
# ── Write control files ───────────────────────────────────────
|
|
cat > "${BUILD}/DEBIAN/control" <<EOF
|
|
Package: ${PKG_NAME}
|
|
Version: ${PKG_VERSION}
|
|
Architecture: ${PKG_ARCH}
|
|
Maintainer: tocmo0nlord
|
|
Depends: python3 (>= 3.10), python3-pip, python3-venv, nginx
|
|
Description: LLM Trainer Dashboard
|
|
Web dashboard for managing LLM training pipelines via SSH.
|
|
Includes document ingestion, QA pair generation, curation,
|
|
training monitor, and an interactive terminal.
|
|
EOF
|
|
|
|
# ── Systemd service ───────────────────────────────────────────
|
|
cat > "${BUILD}/lib/systemd/system/llm-trainer.service" <<EOF
|
|
[Unit]
|
|
Description=LLM Trainer Backend
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=llm-trainer
|
|
Group=llm-trainer
|
|
WorkingDirectory=/opt/llm-trainer/backend
|
|
ExecStart=/opt/llm-trainer/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8080
|
|
Restart=always
|
|
RestartSec=5
|
|
StandardOutput=append:/var/log/llm-trainer/backend.log
|
|
StandardError=append:/var/log/llm-trainer/backend.log
|
|
EnvironmentFile=-/etc/llm-trainer/env
|
|
Environment=PYTHONUNBUFFERED=1
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# ── Nginx config ──────────────────────────────────────────────
|
|
cat > "${BUILD}/etc/nginx/sites-available/llm-trainer" <<'EOF'
|
|
server {
|
|
listen 3000;
|
|
server_name _;
|
|
|
|
root /opt/llm-trainer/frontend;
|
|
index index.html;
|
|
|
|
# Serve React SPA
|
|
location / {
|
|
try_files $uri $uri/ /index.html;
|
|
}
|
|
|
|
# Proxy REST API to FastAPI backend
|
|
location /api/ {
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
# WebSocket support (terminal, log streaming)
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_read_timeout 86400;
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# ── postinst ──────────────────────────────────────────────────
|
|
cat > "${BUILD}/DEBIAN/postinst" <<'POSTINST'
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
# Create system user
|
|
if ! id -u llm-trainer &>/dev/null; then
|
|
useradd --system --no-create-home --shell /usr/sbin/nologin llm-trainer
|
|
fi
|
|
|
|
# Set permissions
|
|
chown -R llm-trainer:llm-trainer /opt/llm-trainer
|
|
chown -R llm-trainer:llm-trainer /var/log/llm-trainer
|
|
|
|
# Write default env config (only on first install)
|
|
if [ ! -f /etc/llm-trainer/env ]; then
|
|
cat > /etc/llm-trainer/env <<'ENV'
|
|
# LLM Trainer configuration
|
|
# Override Ollama URL if it runs on a different host
|
|
OLLAMA_URL=http://localhost:11434
|
|
ENV
|
|
fi
|
|
|
|
# Build Python venv and install deps
|
|
echo "Installing Python dependencies..."
|
|
python3 -m venv /opt/llm-trainer/venv
|
|
/opt/llm-trainer/venv/bin/pip install --quiet --upgrade pip
|
|
/opt/llm-trainer/venv/bin/pip install --quiet -r /opt/llm-trainer/backend/requirements.txt
|
|
chown -R llm-trainer:llm-trainer /opt/llm-trainer/venv
|
|
|
|
# Enable nginx site
|
|
ln -sf /etc/nginx/sites-available/llm-trainer /etc/nginx/sites-enabled/llm-trainer
|
|
nginx -t && systemctl reload nginx || true
|
|
|
|
# Enable and start backend service
|
|
systemctl daemon-reload
|
|
systemctl enable llm-trainer
|
|
systemctl start llm-trainer
|
|
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo " LLM Trainer installed successfully!"
|
|
echo " Open: http://$(hostname -I | awk '{print $1}'):3000"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
POSTINST
|
|
chmod 0755 "${BUILD}/DEBIAN/postinst"
|
|
|
|
# ── prerm ─────────────────────────────────────────────────────
|
|
cat > "${BUILD}/DEBIAN/prerm" <<'PRERM'
|
|
#!/bin/bash
|
|
set -e
|
|
systemctl stop llm-trainer 2>/dev/null || true
|
|
systemctl disable llm-trainer 2>/dev/null || true
|
|
rm -f /etc/nginx/sites-enabled/llm-trainer
|
|
systemctl reload nginx 2>/dev/null || true
|
|
PRERM
|
|
chmod 0755 "${BUILD}/DEBIAN/prerm"
|
|
|
|
# ── postrm ────────────────────────────────────────────────────
|
|
cat > "${BUILD}/DEBIAN/postrm" <<'POSTRM'
|
|
#!/bin/bash
|
|
set -e
|
|
if [ "$1" = "purge" ]; then
|
|
rm -rf /opt/llm-trainer
|
|
rm -rf /var/log/llm-trainer
|
|
rm -f /etc/nginx/sites-available/llm-trainer
|
|
userdel llm-trainer 2>/dev/null || true
|
|
fi
|
|
POSTRM
|
|
chmod 0755 "${BUILD}/DEBIAN/postrm"
|
|
|
|
# ── Build .deb ────────────────────────────────────────────────
|
|
echo ""
|
|
echo "[3/4] Building .deb package..."
|
|
cd "${REPO_ROOT}"
|
|
dpkg-deb --build --root-owner-group "${PKG_DIR}"
|
|
echo " Package built → ${PKG_DIR}.deb"
|
|
|
|
# ── Cleanup ───────────────────────────────────────────────────
|
|
rm -rf "${BUILD}"
|
|
|
|
echo ""
|
|
echo "[4/4] Done!"
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo " Install with:"
|
|
echo " sudo dpkg -i ${PKG_DIR}.deb"
|
|
echo " sudo apt-get install -f # fix any missing deps"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|