Files
silo/scripts/deploy.sh
Forbes 747bae8354 feat(jobs): wire auto-triggering on bom_changed events, add module guard
- Add IsEnabled("jobs") guard to triggerJobs() to skip when module disabled
- Fire bom_changed trigger from HandleAddBOMEntry, HandleUpdateBOMEntry,
  HandleDeleteBOMEntry (matching existing HandleMergeBOM pattern)
- Add 4 integration tests: revision trigger, BOM trigger, filter mismatch,
  module disabled
- Fix AppShell overflow: hidden -> auto so Settings page scrolls
- Clean old frontend assets in deploy script before extracting

Closes #107
2026-02-15 09:43:05 -06:00

163 lines
4.9 KiB
Bash
Executable File

#!/bin/bash
# Deploy Silo to a target host
#
# Usage: ./scripts/deploy.sh [host]
# host defaults to SILO_DEPLOY_TARGET env var, or silo.example.internal
#
# Prerequisites:
# - SSH access to the target host
# - /etc/silo/silod.env must exist on target with credentials filled in
# - PostgreSQL reachable from target (set SILO_DB_HOST to override)
# - MinIO reachable from target (set SILO_MINIO_HOST to override)
#
# Environment variables:
# SILO_DEPLOY_TARGET - target host (default: silo.example.internal)
# SILO_DB_HOST - PostgreSQL host (default: psql.example.internal)
set -euo pipefail
TARGET="${1:-${SILO_DEPLOY_TARGET:-silo.example.internal}}"
DB_HOST="${SILO_DB_HOST:-psql.example.internal}"
DEPLOY_DIR="/opt/silo"
CONFIG_DIR="/etc/silo"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="${SCRIPT_DIR}/.."
echo "=== Silo Deploy to ${TARGET} ==="
# --- Build locally ---
echo "[1/6] Building Go binary..."
cd "$PROJECT_DIR"
GOOS=linux GOARCH=amd64 go build -o silod ./cmd/silod
echo "[2/6] Building React frontend..."
cd "$PROJECT_DIR/web"
npm run build
# --- Package ---
echo "[3/6] Packaging..."
STAGING=$(mktemp -d)
trap "rm -rf $STAGING" EXIT
mkdir -p "$STAGING/bin"
mkdir -p "$STAGING/web"
mkdir -p "$STAGING/schemas"
mkdir -p "$STAGING/migrations"
cp "$PROJECT_DIR/silod" "$STAGING/bin/silod"
cp -r "$PROJECT_DIR/web/dist" "$STAGING/web/dist"
cp "$PROJECT_DIR/schemas/"*.yaml "$STAGING/schemas/"
cp "$PROJECT_DIR/migrations/"*.sql "$STAGING/migrations/"
cp "$PROJECT_DIR/deployments/config.prod.yaml" "$STAGING/config.yaml"
cp "$PROJECT_DIR/deployments/systemd/silod.service" "$STAGING/silod.service"
cp "$PROJECT_DIR/deployments/systemd/silod.env.example" "$STAGING/silod.env.example"
TARBALL=$(mktemp --suffix=.tar.gz)
tar -czf "$TARBALL" -C "$STAGING" .
echo " Package: $(du -h "$TARBALL" | cut -f1)"
# --- Deploy ---
echo "[4/6] Uploading to ${TARGET}..."
scp "$TARBALL" "${TARGET}:/tmp/silo-deploy.tar.gz"
echo "[5/6] Installing on ${TARGET}..."
ssh "$TARGET" bash -s <<'REMOTE'
set -euo pipefail
DEPLOY_DIR="/opt/silo"
CONFIG_DIR="/etc/silo"
# Create directories
sudo mkdir -p "$DEPLOY_DIR/bin" "$DEPLOY_DIR/web" "$DEPLOY_DIR/schemas" "$DEPLOY_DIR/migrations"
sudo mkdir -p "$CONFIG_DIR"
# Stop service if running
if systemctl is-active --quiet silod 2>/dev/null; then
echo " Stopping silod..."
sudo systemctl stop silod
fi
# Clean old frontend assets before extracting
sudo rm -rf "$DEPLOY_DIR/web/dist/assets"
# Extract
echo " Extracting..."
sudo tar -xzf /tmp/silo-deploy.tar.gz -C "$DEPLOY_DIR"
sudo chmod +x "$DEPLOY_DIR/bin/silod"
rm -f /tmp/silo-deploy.tar.gz
# Install config if not present (don't overwrite existing)
if [ ! -f "$CONFIG_DIR/config.yaml" ]; then
echo " Installing default config..."
sudo cp "$DEPLOY_DIR/config.yaml" "$CONFIG_DIR/config.yaml" 2>/dev/null || true
fi
# Install env template if not present
if [ ! -f "$CONFIG_DIR/silod.env" ]; then
echo " WARNING: /etc/silo/silod.env does not exist!"
echo " Copying template..."
sudo cp "$DEPLOY_DIR/silod.env.example" "$CONFIG_DIR/silod.env"
echo " Edit /etc/silo/silod.env with your credentials before starting the service."
fi
# Install systemd service
echo " Installing systemd service..."
sudo cp "$DEPLOY_DIR/silod.service" /etc/systemd/system/silod.service
# Set ownership
sudo chown -R silo:silo "$DEPLOY_DIR" 2>/dev/null || true
sudo chmod 600 "$CONFIG_DIR/silod.env" 2>/dev/null || true
echo " Files installed to $DEPLOY_DIR"
REMOTE
echo "[6/6] Running migrations and starting service..."
ssh "$TARGET" DB_HOST="$DB_HOST" bash -s <<'REMOTE'
set -euo pipefail
DEPLOY_DIR="/opt/silo"
CONFIG_DIR="/etc/silo"
# Source env for migration
if [ -f "$CONFIG_DIR/silod.env" ]; then
set -a
source "$CONFIG_DIR/silod.env"
set +a
fi
# Run migrations
if command -v psql &>/dev/null && [ -n "${SILO_DB_PASSWORD:-}" ]; then
echo " Running migrations..."
for f in "$DEPLOY_DIR/migrations/"*.sql; do
echo " $(basename "$f")"
PGPASSWORD="$SILO_DB_PASSWORD" psql \
-h "$DB_HOST" -p 5432 \
-U silo -d silo \
-f "$f" -q 2>&1 | grep -v "already exists" || true
done
echo " Migrations complete."
else
echo " WARNING: psql not available or SILO_DB_PASSWORD not set, skipping migrations."
echo " Run migrations manually: PGPASSWORD=... psql -h $DB_HOST -U silo -d silo -f /opt/silo/migrations/NNN_name.sql"
fi
# Start service
echo " Starting silod..."
sudo systemctl daemon-reload
sudo systemctl enable silod
sudo systemctl start silod
sleep 2
if systemctl is-active --quiet silod; then
echo " silod is running!"
else
echo " ERROR: silod failed to start. Check: journalctl -u silod -n 50"
exit 1
fi
REMOTE
echo ""
echo "=== Deploy complete ==="
echo " Backend: https://${TARGET} (port 8080)"
echo " React SPA served from /opt/silo/web/dist/"
echo " Logs: ssh ${TARGET} journalctl -u silod -f"