.PHONY: deploy-dev dev-deploy deploy-dev-image dev-remote-setup dev-remote-stop deploy-dev-transcode dev-deploy-transcode deploy-dev-import dev-deploy-import deploy-dev-import-reset dev-deploy-import-reset # Copy this file to Makefile.local and adjust the host/path values for your # private development environment. Makefile.local is intentionally gitignored. DEV_DEPLOY_HOST ?= root@silo-dev.example.invalid DEV_DEPLOY_DIR ?= /opt/git/silo-dev DEV_TRANSCODE_DEPLOY_HOST ?= DEV_TRANSCODE_DEPLOY_DIR ?= /opt/git/silo DEV_IMPORT_MEDIA_ROOT ?= /srv/media DEV_MEDIA_ROOT ?= /srv/media DEV_IMPORT_CATALOG_SEED_PATH ?= /tmp/silo-catalog-seed DEV_PLUGIN_SDK_DIR ?= ../silo-plugin-sdk DEV_LOCAL_IMAGE ?= silo:dev-local DEV_JELLYCOMPAT_DEBUG_LOG ?= /opt/silo/jellycompat-debug.log DEV_COMPOSE_FILE ?= docker-compose.yml RSYNC ?= $(shell command -v rsync || echo /usr/bin/rsync) SILO_IMAGE ?= ghcr.io/silo-server/silo-server:latest RSYNC_EXCLUDE := --exclude='.git' --exclude='node_modules' --exclude='web/dist' \ --exclude='.env' --exclude='.env.*' --exclude='*.md' --exclude='.agents' \ --exclude='.claude' --exclude='.codex' --exclude='.cursor' \ --exclude='.worktrees' --exclude='.playwright-mcp' --exclude='demo' \ --exclude='output' dev-remote-setup: DEV_HOST=$(DEV_DEPLOY_HOST) DEV_DIR=$(DEV_DEPLOY_DIR) ./scripts/dev-remote-setup.sh deploy-dev: @echo "==> Syncing source to $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/Silo/..." $(RSYNC) -az --delete $(RSYNC_EXCLUDE) ./ $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/Silo/ @if [ -f .env ]; then \ $(RSYNC) -az .env $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/Silo/; \ fi @echo "==> Syncing plugin SDK to $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/silo-plugin-sdk/..." $(RSYNC) -az --delete --exclude='.git' $(DEV_PLUGIN_SDK_DIR)/ $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/silo-plugin-sdk/ @echo "==> Stopping air tmux session if running..." ssh $(DEV_DEPLOY_HOST) 'tmux kill-session -t silo-dev 2>/dev/null || true' @echo "==> Normalizing jellycompat debug log path..." ssh $(DEV_DEPLOY_HOST) 'set -e; docker rm -f silo >/dev/null 2>&1 || true; mkdir -p $$(dirname "$(DEV_JELLYCOMPAT_DEBUG_LOG)"); if [ -d "$(DEV_JELLYCOMPAT_DEBUG_LOG)" ]; then if ! rmdir "$(DEV_JELLYCOMPAT_DEBUG_LOG)" 2>/dev/null; then mv "$(DEV_JELLYCOMPAT_DEBUG_LOG)" "$(DEV_JELLYCOMPAT_DEBUG_LOG).dir.$$(date +%s)"; fi; fi; touch "$(DEV_JELLYCOMPAT_DEBUG_LOG)"' @echo "==> Building $(DEV_LOCAL_IMAGE) on remote..." ssh $(DEV_DEPLOY_HOST) 'cd $(DEV_DEPLOY_DIR)/Silo && docker buildx build \ -f Dockerfile.dev \ --build-context silo_plugin_sdk=$(DEV_DEPLOY_DIR)/silo-plugin-sdk \ -t $(DEV_LOCAL_IMAGE) \ --load \ .' @echo "==> Starting services via Docker Compose..." ssh $(DEV_DEPLOY_HOST) 'cd $(DEV_DEPLOY_DIR)/Silo && SILO_IMAGE=$(DEV_LOCAL_IMAGE) MEDIA_ROOT="$(DEV_MEDIA_ROOT)" MEDIA_CONTAINER_ROOT="$(DEV_MEDIA_ROOT)" docker compose -f $(DEV_COMPOSE_FILE) up -d --force-recreate silo' @echo "==> Pruning old images..." ssh $(DEV_DEPLOY_HOST) 'docker image prune -f --filter "dangling=true"' @echo "==> Verifying deployment..." @sleep 5 ssh $(DEV_DEPLOY_HOST) 'docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"' @echo "==> Deploy complete (local build)." dev-deploy: deploy-dev deploy-dev-image: @echo "==> Syncing compose config to $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/Silo/..." $(RSYNC) -az $(DEV_COMPOSE_FILE) $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/Silo/ $(RSYNC) -az postgres/ $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/Silo/postgres/ @if [ -f .env ]; then \ $(RSYNC) -az .env $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/Silo/; \ fi @echo "==> Stopping air tmux session if running..." ssh $(DEV_DEPLOY_HOST) 'tmux kill-session -t silo-dev 2>/dev/null || true' @echo "==> Normalizing jellycompat debug log path..." ssh $(DEV_DEPLOY_HOST) 'set -e; docker rm -f silo >/dev/null 2>&1 || true; mkdir -p $$(dirname "$(DEV_JELLYCOMPAT_DEBUG_LOG)"); if [ -d "$(DEV_JELLYCOMPAT_DEBUG_LOG)" ]; then if ! rmdir "$(DEV_JELLYCOMPAT_DEBUG_LOG)" 2>/dev/null; then mv "$(DEV_JELLYCOMPAT_DEBUG_LOG)" "$(DEV_JELLYCOMPAT_DEBUG_LOG).dir.$$(date +%s)"; fi; fi; touch "$(DEV_JELLYCOMPAT_DEBUG_LOG)"' @echo "==> Pulling $(SILO_IMAGE) on remote..." ssh $(DEV_DEPLOY_HOST) 'docker pull $(SILO_IMAGE)' @echo "==> Starting services via Docker Compose..." ssh $(DEV_DEPLOY_HOST) 'cd $(DEV_DEPLOY_DIR)/Silo && MEDIA_ROOT="$(DEV_MEDIA_ROOT)" MEDIA_CONTAINER_ROOT="$(DEV_MEDIA_ROOT)" docker compose -f $(DEV_COMPOSE_FILE) up -d --force-recreate silo' @echo "==> Verifying deployment..." @sleep 5 ssh $(DEV_DEPLOY_HOST) 'docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"' @echo "==> Deploy complete (image pull)." dev-remote-stop: @echo "==> Stopping air tmux session if running..." ssh $(DEV_DEPLOY_HOST) 'tmux kill-session -t silo-dev 2>/dev/null || true' @echo "==> Stopping dev stack..." ssh $(DEV_DEPLOY_HOST) 'cd $(DEV_DEPLOY_DIR)/Silo && docker compose -f $(DEV_COMPOSE_FILE) down' @echo "==> Done." deploy-dev-transcode: @if [ -z "$(strip $(DEV_TRANSCODE_DEPLOY_HOST))" ]; then \ echo "DEV_TRANSCODE_DEPLOY_HOST is not set; skipping remote transcode deploy."; \ exit 0; \ fi ssh $(DEV_TRANSCODE_DEPLOY_HOST) 'docker pull $(SILO_IMAGE)' $(RSYNC) -az docker-compose.remote-transcode.yml $(DEV_TRANSCODE_DEPLOY_HOST):$(DEV_TRANSCODE_DEPLOY_DIR)/ ssh $(DEV_TRANSCODE_DEPLOY_HOST) 'set -e; cd $(DEV_TRANSCODE_DEPLOY_DIR) && docker compose -f docker-compose.remote-transcode.yml up -d --force-recreate silo-transcode && sleep 5 && docker compose -f docker-compose.remote-transcode.yml ps && docker inspect -f "{{.State.Status}}" silo-transcode | grep -qx running || { echo; echo "silo-transcode container is not running; recent logs:"; docker logs silo-transcode --tail 120; exit 1; }' ssh $(DEV_TRANSCODE_DEPLOY_HOST) 'docker image prune -f --filter "dangling=true"' dev-deploy-transcode: deploy-dev-transcode deploy-dev-import: $(RSYNC) -az $(DEV_COMPOSE_FILE) $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/Silo/ $(RSYNC) -az postgres/ $(DEV_DEPLOY_HOST):$(DEV_DEPLOY_DIR)/Silo/postgres/ ssh $(DEV_DEPLOY_HOST) 'docker pull $(SILO_IMAGE)' ssh $(DEV_DEPLOY_HOST) 'cd $(DEV_DEPLOY_DIR)/Silo && MEDIA_ROOT="$(DEV_IMPORT_MEDIA_ROOT)" MEDIA_CONTAINER_ROOT="$(DEV_IMPORT_MEDIA_ROOT)" CATALOG_SEED_PATH="$(DEV_IMPORT_CATALOG_SEED_PATH)" docker compose -p silo-import -f $(DEV_COMPOSE_FILE) up -d --force-recreate' ssh $(DEV_DEPLOY_HOST) 'docker image prune -f --filter "dangling=true"' dev-deploy-import: deploy-dev-import deploy-dev-import-reset: ssh $(DEV_DEPLOY_HOST) 'cd $(DEV_DEPLOY_DIR)/Silo && docker compose -p silo-import -f $(DEV_COMPOSE_FILE) down -v --remove-orphans || true' $(MAKE) deploy-dev-import dev-deploy-import-reset: deploy-dev-import-reset