Files
silo/scripts/deploy.sh
Forbes 50923cf56d feat: production release with React SPA, file attachments, and deploy tooling
Backend:
- Add file_handlers.go: presigned upload/download for item attachments
- Add item_files.go: item file and thumbnail DB operations
- Add migration 011: item_files table and thumbnail_key column
- Update items/projects/relationships DB with extended field support
- Update routes: React SPA serving from web/dist, file upload endpoints
- Update auth handlers and middleware for cookie + bearer token auth
- Remove Go HTML templates (replaced by React SPA)
- Update storage client for presigned URL generation

Frontend:
- Add TagInput component for tag/keyword entry
- Add SVG assets for Silo branding and UI icons
- Update API client and types for file uploads, auth, extended fields
- Update AuthContext for session-based auth flow
- Update LoginPage, ProjectsPage, SchemasPage, SettingsPage
- Fix tsconfig.node.json

Deployment:
- Update config.prod.yaml: single-binary SPA layout at /opt/silo
- Update silod.service: ReadOnlyPaths for /opt/silo
- Add scripts/deploy.sh: build, package, ship, migrate, start
- Update docker-compose.yaml and Dockerfile
- Add frontend-spec.md design document
2026-02-07 13:35:22 -06:00

155 lines
4.6 KiB
Bash
Executable File

#!/bin/bash
# Deploy Silo to silo.kindred.internal
#
# Usage: ./scripts/deploy.sh [host]
# host defaults to silo.kindred.internal
#
# Prerequisites:
# - SSH access to the target host
# - /etc/silo/silod.env must exist on target with credentials filled in
# - PostgreSQL reachable from target at psql.kindred.internal
# - MinIO reachable from target at minio.kindred.internal
set -euo pipefail
TARGET="${1:-silo.kindred.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
# 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" 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 psql.kindred.internal -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 psql.kindred.internal -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"