# Drevalis Creator Studio — Windows installer (PowerShell 5.1+). # # One-liner (paste into PowerShell as Administrator or a regular user with # Docker Desktop already running): # # iwr -useb https://drevalis.com/install.ps1 | iex # # What this script does: # 1. Checks you have Docker Desktop installed + running (WSL2 backend) # 2. Creates %USERPROFILE%\Drevalis (or $env:INSTALL_DIR if set) # 3. Writes docker-compose.yml pulling official images from GHCR # 4. Generates an ENCRYPTION_KEY (Fernet) and writes .env # 5. Pulls images and starts the stack # 6. Opens http://localhost:3000 in your default browser # # Requirements: # - Windows 10 21H2+ or Windows 11 # - Docker Desktop (https://www.docker.com/products/docker-desktop/) # - WSL2 enabled (Docker Desktop enables this automatically) #Requires -Version 5.1 $ErrorActionPreference = 'Stop' # ---- Configuration (override via $env:... before running) --------------- $InstallDir = if ($env:INSTALL_DIR) { $env:INSTALL_DIR } else { Join-Path $env:USERPROFILE 'Drevalis' } $ImageRegistry = if ($env:IMAGE_REGISTRY) { $env:IMAGE_REGISTRY } else { 'ghcr.io/drevaliscs' } $ImageTag = if ($env:IMAGE_TAG) { $env:IMAGE_TAG } else { 'stable' } $LicenseServerUrl = if ($env:LICENSE_SERVER_URL) { $env:LICENSE_SERVER_URL } else { 'https://license.drevalis.com' } # ---- Pretty output ------------------------------------------------------ function Write-Step { param($msg) Write-Host "`n▶ $msg" -ForegroundColor Green } function Write-Warn { param($msg) Write-Host "! $msg" -ForegroundColor Yellow } function Write-Fail { param($msg) Write-Host "✕ $msg" -ForegroundColor Red # ``exit`` inside a script piped via ``iwr | iex`` is easy to miss — # ``throw`` always surfaces as a visible terminating error. throw $msg } # ---- 1. Docker Desktop --------------------------------------------------- Write-Step "Checking Docker Desktop" try { $dockerVersion = & docker version --format '{{.Server.Version}}' 2>$null if ([string]::IsNullOrWhiteSpace($dockerVersion)) { throw "docker returned nothing" } Write-Host " Docker $dockerVersion ok" } catch { Write-Warn "Docker is not running or not installed." Write-Host "" Write-Host " 1. Install Docker Desktop: https://www.docker.com/products/docker-desktop/" Write-Host " 2. Launch Docker Desktop and wait for the whale icon in the tray" Write-Host " 3. Re-run this installer" Write-Host "" Write-Fail "Aborting — Docker Desktop is required." } try { & docker compose version | Out-Null } catch { Write-Fail "Docker Compose plugin is missing. Update Docker Desktop to the latest version." } # ---- 2. Install directory ----------------------------------------------- Write-Step "Preparing $InstallDir" if (-not (Test-Path $InstallDir)) { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null } Set-Location $InstallDir Write-Host " Working directory: $InstallDir" # ---- 3. docker-compose.yml ---------------------------------------------- Write-Step "Writing docker-compose.yml" $composeYaml = @" # Drevalis Creator Studio — generated by install.ps1 # Image tag: $ImageTag # Pin the project name so ``docker compose`` on the host and the # updater sidecar ( which runs ``compose`` from a different dir) both # target the same stack. Without this, renaming the install folder # silently breaks in-app updates. name: drevalis services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_USER: drevalis POSTGRES_PASSWORD: drevalis POSTGRES_DB: drevalis volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U drevalis"] interval: 5s retries: 20 redis: image: redis:7-alpine restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s retries: 20 app: image: $ImageRegistry/creator-studio-app:$ImageTag restart: unless-stopped ports: - "8000:8000" env_file: .env environment: DATABASE_URL: postgresql+asyncpg://drevalis:drevalis@postgres:5432/drevalis REDIS_URL: redis://redis:6379/0 PYTHONPATH: /app/src volumes: - ./storage:/app/storage - updater_shared:/shared working_dir: /app # Run migrations inline before uvicorn. Previous setup used a separate # migrate one-shot with depends_on.service_completed_successfully, but # that deadlocked the stack (502 on every endpoint) when the one-shot # failed for any reason. Inline is more robust and surfaces alembic # errors in the same log stream the operator is already watching. command: sh -c "alembic upgrade head && exec python -m uvicorn shortsfactory.main:app --host 0.0.0.0 --port 8000 --workers 4" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 10s timeout: 5s retries: 3 start_period: 60s depends_on: postgres: { condition: service_healthy } redis: { condition: service_healthy } worker: image: $ImageRegistry/creator-studio-app:$ImageTag restart: unless-stopped env_file: .env environment: DATABASE_URL: postgresql+asyncpg://drevalis:drevalis@postgres:5432/drevalis REDIS_URL: redis://redis:6379/0 PYTHONPATH: /app/src volumes: - ./storage:/app/storage command: python -m arq shortsfactory.workers.settings.WorkerSettings depends_on: postgres: { condition: service_healthy } redis: { condition: service_healthy } app: { condition: service_healthy } frontend: image: $ImageRegistry/creator-studio-frontend:$ImageTag restart: unless-stopped ports: - "3000:3000" depends_on: - app updater: image: $ImageRegistry/creator-studio-updater:$ImageTag restart: unless-stopped volumes: - //var/run/docker.sock:/var/run/docker.sock - .:/project - updater_shared:/shared environment: POLL_SECONDS: "15" # Docker Compose derives the project name from the compose file's # parent directory basename. Inside the updater container that's # ``/project`` → the sidecar would invoke ``docker compose up -d`` # against a non-existent stack named ``project`` and the real one # keeps running the old image. We pin the same name the host uses. COMPOSE_PROJECT_NAME: "drevalis" volumes: postgres_data: updater_shared: "@ Set-Content -Path (Join-Path $InstallDir 'docker-compose.yml') -Value $composeYaml -Encoding UTF8 # ---- 4. .env ------------------------------------------------------------- $envPath = Join-Path $InstallDir '.env' if (Test-Path $envPath) { Write-Warn ".env already exists — keeping your existing values" } else { Write-Step "Generating encryption key" # Fernet key = 32 random bytes, URL-safe base64 encoded. $bytes = New-Object byte[] 32 [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) $b64 = [Convert]::ToBase64String($bytes).Replace('+', '-').Replace('/', '_').TrimEnd('=') + '=' $envContent = @" # Drevalis Creator Studio — generated $(Get-Date -Format s) # # Keep this file private. Secrets never leave your machine. ENCRYPTION_KEY=$b64 LICENSE_SERVER_URL=$LicenseServerUrl # Optional API auth token. Leave empty for single-user local dev. API_AUTH_TOKEN= # DEBUG=false # STORAGE_BASE_PATH=./storage "@ Set-Content -Path $envPath -Value $envContent -Encoding UTF8 Write-Host " Wrote $envPath" } # ---- 5. Pull + start ----------------------------------------------------- Write-Step "Pulling images (first run can take several minutes)" & docker compose pull if ($LASTEXITCODE -ne 0) { Write-Host "" Write-Host " Common causes:" -ForegroundColor Yellow Write-Host " - GHCR packages not yet public. See https://drevalis.com/download#troubleshoot" Write-Host " - Docker Desktop not signed in / rate-limited (docker login)" Write-Host " - No internet / corporate proxy blocking ghcr.io" Write-Fail "docker compose pull failed — see output above" } Write-Step "Starting the stack" & docker compose up -d if ($LASTEXITCODE -ne 0) { Write-Fail "docker compose up failed" } Write-Step "Waiting for the app to become healthy" $ok = $false for ($i = 1; $i -le 60; $i++) { try { $resp = Invoke-WebRequest -Uri 'http://localhost:8000/health' -UseBasicParsing -TimeoutSec 3 if ($resp.StatusCode -eq 200) { $ok = $true; break } } catch { } Start-Sleep -Seconds 2 } if ($ok) { Write-Host " App is healthy" } else { Write-Warn "App didn't answer /health within 2 minutes. Run 'docker compose logs app' to diagnose." } # ---- 6. Done ------------------------------------------------------------- Write-Host "" Write-Host "══════════════════════════════════════════════════════════════" -ForegroundColor Green Write-Host " Drevalis Creator Studio is installed" -ForegroundColor Green Write-Host "══════════════════════════════════════════════════════════════" -ForegroundColor Green Write-Host "" Write-Host " Open: http://localhost:3000" Write-Host " Backend API: http://localhost:8000/docs" Write-Host " Install dir: $InstallDir" Write-Host "" Write-Host " Next steps:" Write-Host " 1. Open http://localhost:3000 in your browser" Write-Host " 2. Paste your license key from the welcome email" Write-Host " 3. Settings → ComfyUI Servers → add your ComfyUI URL" Write-Host " 4. Settings → LLM Configs → add your LLM endpoint" Write-Host "" Write-Host " Updates: in-app, Settings → Updates → 'Update now'" Write-Host " Support: https://drevalis.com/download · support@drevalis.com" Write-Host "" # Open the app in the default browser try { Start-Process 'http://localhost:3000' } catch { }