Add VM deployment automation

Created turnkey deployment for Hyper-V VMs and fresh Windows installs.

FILES:
- deploy-to-vm.ps1: Automated PowerShell deployment script
  - Checks prerequisites
  - Builds images (offline packages)
  - Starts all services
  - Initializes database
  - Sets admin password
  - One command deployment

- DEPLOY-TO-VM.md: Complete deployment guide
  - Multiple copy methods
  - Manual deployment steps
  - Troubleshooting
  - Offline deployment notes

USAGE:
  .\deploy-to-vm.ps1 -SiteName 'west-jefferson' -AdminPassword 'SecurePass!'

TIMELINE: 5 minutes from repo copy to running system
This commit is contained in:
Proudlock
2026-09-07 00:50:10 -04:00
parent 783b5d3806
commit bdc32fd134
2 changed files with 313 additions and 0 deletions

177
DEPLOY-TO-VM.md Normal file
View File

@@ -0,0 +1,177 @@
# Deploy ShopDB to Hyper-V VM
Quick guide to deploy the Docker stack to a Windows 11 Hyper-V VM.
## Prerequisites on VM
1. **Docker Desktop** installed and running
2. **Git Bash** or **WSL** installed (for bash commands)
3. **PowerShell** (already on Windows 11)
## Step 1: Copy Files to VM
**Method A: Shared Folder**
```powershell
# On host PC, share the repo
# Then on VM, copy from network share
Copy-Item -Recurse \\host-pc\shopdb-flask C:\shopdb
```
**Method B: Git Clone**
```bash
# On VM
cd C:\
git clone git@github.com:ge-aero/shopdb-flask.git
cd shopdb-flask
git checkout wj
```
**Method C: Export/Import**
```powershell
# On host PC
Compress-Archive -Path C:\Users\270015376\Documents\github\flask-shopdb -DestinationPath C:\shopdb-deploy.zip
# Copy shopdb-deploy.zip to VM, then on VM:
Expand-Archive -Path C:\shopdb-deploy.zip -DestinationPath C:\shopdb
```
## Step 2: Run Deployment Script
```powershell
# On VM, open PowerShell as Administrator
cd C:\shopdb
# Option 1: Quick deploy with defaults
.\deploy-to-vm.ps1
# Option 2: Specify site name and password
.\deploy-to-vm.ps1 -SiteName "west-jefferson" -AdminPassword "YourSecurePassword123!"
# Option 3: See all options
.\deploy-to-vm.ps1 -Help
```
## What the Script Does
1. ✅ Checks Docker Desktop is running
2. ✅ Creates .env from template
3. ✅ Builds frontend (if needed)
4. ✅ Builds Docker images (2-3 minutes, offline packages)
5. ✅ Starts all services (db + api + backup)
6. ✅ Runs database migrations
7. ✅ Sets admin password
## Manual Deployment (Alternative)
If you prefer to run commands manually:
```bash
# 1. Create environment
cp .env.docker-dev .env
# 2. Build frontend (if not already built)
cd frontend
npm ci
npm run build
cd ..
# 3. Build and start Docker stack
docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml --profile backup build
docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml --profile backup up -d
# 4. Initialize database
docker exec flask-shopdb-api-1 flask db upgrade
# 5. Reset admin password (optional)
docker exec flask-shopdb-db-1 mysql -uroot -proot-dev-password-not-for-production shopdb_flask -e \
"UPDATE users SET passwordhash='scrypt:32768:8:1\$B4LhpPrM6qxijInK\$b99b47a0bb5fd45761a1823f401f70a3ceccf71a5d38dc433378aa07426ef8eaf7e679ff3dd7b0ce9772600f83368ac642e9419b9beaa49f87483318bae34547', failedlogins=0, lockeduntil=NULL WHERE username='admin';"
# (Password is: admin)
```
## Access
**URL:** https://localhost:5001 (or https://vm-ip-address:5001 from host)
**Login:**
- Username: `admin`
- Password: Whatever you set in deploy script (default: `ChangeMe123!`)
## Verify Deployment
```powershell
# Check all services are running
docker ps
# Should see:
# - flask-shopdb-db-1
# - flask-shopdb-api-1
# - flask-shopdb-backup-store-1
# - flask-shopdb-backup-watcher-1
# View logs
docker logs -f flask-shopdb-api-1
# Test backup
# Go to https://localhost:5001/settings/shopdbbackups
# Click "Back Up Now"
# Wait 30 seconds, should show as "Checked"
```
## Troubleshooting
**Docker not running:**
```powershell
# Start Docker Desktop manually
& "C:\Program Files\Docker\Docker\Docker Desktop.exe"
Start-Sleep -Seconds 30
```
**Port 5001 already in use:**
```powershell
# Edit .env and change API_PORT
# API_PORT=5002
```
**Can't access from host PC:**
```powershell
# On VM, check Windows Firewall
New-NetFirewallRule -DisplayName "ShopDB" -Direction Inbound -LocalPort 5001 -Protocol TCP -Action Allow
```
**SSL certificate warnings:**
- Normal for self-signed certs
- Click "Advanced" → "Proceed to localhost (unsafe)"
## Stop/Start
```bash
# Stop
docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml down
# Start (after first deployment)
docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml --profile backup up -d
```
## Update After Code Changes
```bash
# 1. Pull latest code
git pull origin wj
# 2. Rebuild frontend (if changed)
cd frontend && npm run build && cd ..
# 3. Rebuild and restart
docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml build
docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml up -d
```
## Offline Deployment Notes
**This deployment works completely offline** because:
- ✅ Frontend pre-built and checked into repo
- ✅ Restic binary pre-downloaded (8.6MB in repo)
- ✅ No npm/pip downloads during Docker build
- ✅ All dependencies baked into images
Perfect for air-gapped or restricted network environments!

136
deploy-to-vm.ps1 Normal file
View File

@@ -0,0 +1,136 @@
# ShopDB Docker Deployment Script for Hyper-V VM
# Run this ON THE VM after copying the repo
param(
[string]$SiteName = "shopdb-vm",
[string]$AdminPassword = "ChangeMe123!",
[switch]$Help
)
if ($Help) {
Write-Host @"
ShopDB Docker Deployment Script
Usage:
.\deploy-to-vm.ps1 [-SiteName <name>] [-AdminPassword <password>]
Parameters:
-SiteName Site identifier (default: shopdb-vm)
-AdminPassword Initial admin password (default: ChangeMe123!)
-Help Show this help
Example:
.\deploy-to-vm.ps1 -SiteName "west-jefferson" -AdminPassword "SecurePass123!"
Prerequisites:
- Docker Desktop installed and running
- Git Bash or WSL available
"@
exit 0
}
$ErrorActionPreference = "Stop"
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "ShopDB Docker Deployment" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# Check Docker is running
Write-Host "[1/8] Checking Docker Desktop..." -ForegroundColor Yellow
try {
docker ps | Out-Null
Write-Host " OK: Docker is running" -ForegroundColor Green
} catch {
Write-Host " ERROR: Docker Desktop is not running" -ForegroundColor Red
Write-Host " Please start Docker Desktop and try again" -ForegroundColor Red
exit 1
}
# Check if we're in the repo
Write-Host "[2/8] Checking repository..." -ForegroundColor Yellow
if (-not (Test-Path "docker-compose.prebuilt.yml")) {
Write-Host " ERROR: Not in shopdb-flask directory" -ForegroundColor Red
Write-Host " Please cd to the repository root" -ForegroundColor Red
exit 1
}
Write-Host " OK: In repository root" -ForegroundColor Green
# Create .env if it doesn't exist
Write-Host "[3/8] Configuring environment..." -ForegroundColor Yellow
if (-not (Test-Path ".env")) {
Copy-Item ".env.docker-dev" ".env"
Write-Host " Created .env from template" -ForegroundColor Green
# Update site name
(Get-Content ".env") -replace 'SHOPDB_SITE=shopdb-dev', "SHOPDB_SITE=$SiteName" | Set-Content ".env"
(Get-Content ".env") -replace 'SHOPDB_ENV_LABEL=DOCKER-DEV', "SHOPDB_ENV_LABEL=$SiteName" | Set-Content ".env"
Write-Host " Configured for site: $SiteName" -ForegroundColor Green
} else {
Write-Host " OK: .env already exists" -ForegroundColor Green
}
# Build frontend if needed
Write-Host "[4/8] Checking frontend build..." -ForegroundColor Yellow
if (-not (Test-Path "frontend/dist/index.html")) {
Write-Host " Building frontend (this may take 2-3 minutes)..." -ForegroundColor Yellow
Set-Location frontend
npm ci
npm run build
Set-Location ..
Write-Host " OK: Frontend built" -ForegroundColor Green
} else {
Write-Host " OK: Frontend already built" -ForegroundColor Green
}
# Build Docker images
Write-Host "[5/8] Building Docker images..." -ForegroundColor Yellow
Write-Host " This will take 2-3 minutes (using offline packages)..." -ForegroundColor Yellow
docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml build
Write-Host " OK: Images built" -ForegroundColor Green
# Start services
Write-Host "[6/8] Starting services..." -ForegroundColor Yellow
docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml --profile backup up -d
Start-Sleep -Seconds 10
Write-Host " OK: Services started" -ForegroundColor Green
# Run migrations
Write-Host "[7/8] Initializing database..." -ForegroundColor Yellow
docker exec flask-shopdb-api-1 flask db upgrade
Write-Host " OK: Database initialized" -ForegroundColor Green
# Set admin password
Write-Host "[8/8] Setting admin password..." -ForegroundColor Yellow
$passwordHash = docker exec flask-shopdb-api-1 python -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('$AdminPassword'))"
$dbPassword = (Get-Content ".env" | Select-String "MYSQL_ROOT_PASSWORD=(.+)" | ForEach-Object { $_.Matches.Groups[1].Value })
docker exec flask-shopdb-db-1 mysql -uroot -p$dbPassword shopdb_flask -e @"
UPDATE users SET
passwordhash = '$passwordHash',
failedlogins = 0,
lockeduntil = NULL
WHERE username = 'admin';
"@
Write-Host " OK: Admin password set" -ForegroundColor Green
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host "Deployment Complete!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host "Access ShopDB at: https://localhost:5001" -ForegroundColor Cyan
Write-Host ""
Write-Host "Login credentials:" -ForegroundColor Yellow
Write-Host " Username: admin" -ForegroundColor White
Write-Host " Password: $AdminPassword" -ForegroundColor White
Write-Host ""
Write-Host "Services running:" -ForegroundColor Yellow
docker ps --format "table {{.Names}}\t{{.Status}}"
Write-Host ""
Write-Host "To view logs:" -ForegroundColor Yellow
Write-Host " docker logs -f flask-shopdb-api-1" -ForegroundColor White
Write-Host ""
Write-Host "To stop:" -ForegroundColor Yellow
Write-Host " docker compose -f docker-compose.prebuilt.yml -f docker-compose.backup.yml down" -ForegroundColor White
Write-Host ""