Optimize Windows deployment scripts: build cache, console integration, and .env.example handling
Build-ROA2WEB.ps1 improvements:
- Add intelligent npm node_modules caching (saves 2-5 minutes on repeated builds)
- Cache stored outside deploy-package in .build-cache/ directory
- Add Clean Cache menu option ([C]) and -CleanCache parameter
- Include ROA2WEB-Console.ps1 in deployment package
- Update README workflow to use unified console (interactive and CLI modes)
- Remove obsolete script references (Manage-ROA2WEB.ps1, Deploy-*.ps1)
- Fix .env.example copying for backend and telegram-bot (copy from source instead of hardcoded template)
ROA2WEB-Console.ps1 improvements:
- Fix PowerShell parsing error: ${ComponentName}: instead of $ComponentName:
- Add .env.example copying in Update-BackendFiles function
- Add .env.example copying in Update-TelegramBotFiles function
- Keep .env.example synchronized with development templates
.gitignore:
- Add .build-cache/ to prevent committing npm cache directory
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -354,6 +354,11 @@ deployment/windows/deploy-package/
|
||||
deploy-package/
|
||||
**/deploy-package/
|
||||
|
||||
# Build cache (npm node_modules cache for faster builds)
|
||||
deployment/windows/.build-cache/
|
||||
.build-cache/
|
||||
**/.build-cache/
|
||||
|
||||
# Deployment logs and temporary files
|
||||
deployment/windows/scripts/*.log
|
||||
deployment/windows/temp/
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
.PARAMETER Clean
|
||||
Clean output directory before building (default: true)
|
||||
|
||||
.PARAMETER CleanCache
|
||||
Clean build cache (cached node_modules) and exit
|
||||
|
||||
.EXAMPLE
|
||||
.\Build-ROA2WEB.ps1
|
||||
Shows interactive menu to select components to build
|
||||
@@ -53,6 +56,10 @@
|
||||
.\Build-ROA2WEB.ps1 -Component All -OutputPath "D:\deployments\roa2web-$(Get-Date -Format 'yyyyMMdd')"
|
||||
Build complete package to custom output path
|
||||
|
||||
.EXAMPLE
|
||||
.\Build-ROA2WEB.ps1 -CleanCache
|
||||
Clean build cache to free disk space
|
||||
|
||||
.NOTES
|
||||
Author: ROA2WEB Team
|
||||
Version: 2.0 (Unified Build Script)
|
||||
@@ -67,7 +74,8 @@ param(
|
||||
[string]$OutputPath = "./deploy-package",
|
||||
[string]$ServerHost = "",
|
||||
[string]$ServerPath = "",
|
||||
[bool]$Clean = $true
|
||||
[bool]$Clean = $true,
|
||||
[switch]$CleanCache
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
@@ -119,6 +127,46 @@ function Resolve-FullPath {
|
||||
return $fullPath
|
||||
}
|
||||
|
||||
function Clear-BuildCache {
|
||||
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
||||
Write-Host " CLEAN BUILD CACHE" -ForegroundColor Cyan
|
||||
Write-Host ("=" * 70) -ForegroundColor Cyan
|
||||
|
||||
$scriptDir = Split-Path -Parent $PSScriptRoot
|
||||
$cacheDir = Join-Path $scriptDir ".build-cache"
|
||||
|
||||
if (-not (Test-Path $cacheDir)) {
|
||||
Write-Warning "No build cache found at: $cacheDir"
|
||||
Write-Host "`nNothing to clean." -ForegroundColor Gray
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
# Calculate cache size
|
||||
$cacheFiles = Get-ChildItem -Path $cacheDir -Recurse -File -ErrorAction SilentlyContinue
|
||||
$cacheSize = ($cacheFiles | Measure-Object -Property Length -Sum).Sum / 1MB
|
||||
|
||||
Write-Host "`nCache location: $cacheDir" -ForegroundColor Gray
|
||||
Write-Host "Cache size: $([math]::Round($cacheSize, 2)) MB" -ForegroundColor Gray
|
||||
Write-Host "Files: $(($cacheFiles).Count)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "Are you sure you want to delete the build cache? [Y/N]: " -ForegroundColor Yellow -NoNewline
|
||||
$confirmation = Read-Host
|
||||
|
||||
if ($confirmation.ToUpper() -eq "Y") {
|
||||
Write-Step "Removing build cache..."
|
||||
Remove-Item -Path $cacheDir -Recurse -Force -ErrorAction Stop
|
||||
Write-Success "Build cache cleared successfully"
|
||||
Write-Success "Freed $([math]::Round($cacheSize, 2)) MB of disk space"
|
||||
} else {
|
||||
Write-Host "`nCache cleanup cancelled." -ForegroundColor Yellow
|
||||
}
|
||||
} catch {
|
||||
Write-Host "`n[ERROR] Failed to clear cache: $_" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
function Show-BuildMenu {
|
||||
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
||||
Write-Host " ROA2WEB - Build Component Selection Menu" -ForegroundColor Cyan
|
||||
@@ -138,6 +186,9 @@ function Show-BuildMenu {
|
||||
Write-Host " [4] Telegram Bot Only" -ForegroundColor White
|
||||
Write-Host " (Telegram bot standalone package)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host " [C] Clean Build Cache" -ForegroundColor Yellow
|
||||
Write-Host " (Remove cached node_modules to free disk space)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host " [Q] Quit" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 70) -ForegroundColor Cyan
|
||||
@@ -151,12 +202,18 @@ function Show-BuildMenu {
|
||||
"2" { return "Frontend" }
|
||||
"3" { return "Backend" }
|
||||
"4" { return "TelegramBot" }
|
||||
"C" {
|
||||
Clear-BuildCache
|
||||
Write-Host "`nPress any key to return to menu..." -ForegroundColor Gray
|
||||
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|
||||
return Show-BuildMenu
|
||||
}
|
||||
"Q" {
|
||||
Write-Host "`nBuild cancelled by user." -ForegroundColor Yellow
|
||||
exit 0
|
||||
}
|
||||
default {
|
||||
Write-Host "Invalid choice. Please select 1-4 or Q." -ForegroundColor Red
|
||||
Write-Host "Invalid choice. Please select 1-4, C or Q." -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
} while ($true)
|
||||
@@ -210,6 +267,13 @@ function Build-Frontend {
|
||||
New-Item -ItemType Directory -Path $tempBuildDir -Force | Out-Null
|
||||
Write-Success "Created temp build directory (isolated from WSL)"
|
||||
|
||||
# Create cache directory for node_modules (OUTSIDE deploy-package)
|
||||
$scriptDir = Split-Path -Parent $PSScriptRoot
|
||||
$cacheDir = Join-Path $scriptDir ".build-cache"
|
||||
if (-not (Test-Path $cacheDir)) {
|
||||
New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# Copy frontend sources to temp (exclude node_modules, dist, .git)
|
||||
Write-Step "Copying frontend sources to temp directory..."
|
||||
$excludeDirs = @("node_modules", "dist", ".git", "__pycache__", ".vscode", ".idea")
|
||||
@@ -257,19 +321,66 @@ function Build-Frontend {
|
||||
# Build in temp directory
|
||||
Push-Location $tempBuildDir
|
||||
try {
|
||||
# Install dependencies (including devDependencies for Vite)
|
||||
Write-Step "Installing npm dependencies (Windows binaries)..."
|
||||
# Clear NODE_ENV to ensure devDependencies (vite, etc.) are installed
|
||||
Remove-Item Env:\NODE_ENV -ErrorAction SilentlyContinue
|
||||
npm install | Out-Default
|
||||
# Check if dependencies need to be reinstalled
|
||||
$needsInstall = $true
|
||||
$cachedNodeModules = Join-Path $cacheDir "node_modules"
|
||||
$cachedPackageJson = Join-Path $cacheDir "package.json"
|
||||
$cachedPackageLock = Join-Path $cacheDir "package-lock.json"
|
||||
|
||||
$nodeModulesPath = Join-Path $tempBuildDir "node_modules"
|
||||
if (-not (Test-Path $nodeModulesPath)) {
|
||||
throw "npm install failed: node_modules not created"
|
||||
$currentPackageJson = Join-Path $tempBuildDir "package.json"
|
||||
$currentPackageLock = Join-Path $tempBuildDir "package-lock.json"
|
||||
|
||||
if ((Test-Path $cachedNodeModules) -and (Test-Path $cachedPackageJson)) {
|
||||
# Compare package.json hashes
|
||||
$currentHash = (Get-FileHash $currentPackageJson -Algorithm SHA256).Hash
|
||||
$cachedHash = (Get-FileHash $cachedPackageJson -Algorithm SHA256).Hash
|
||||
|
||||
# Also compare package-lock.json if it exists
|
||||
$lockMatches = $true
|
||||
if ((Test-Path $currentPackageLock) -and (Test-Path $cachedPackageLock)) {
|
||||
$currentLockHash = (Get-FileHash $currentPackageLock -Algorithm SHA256).Hash
|
||||
$cachedLockHash = (Get-FileHash $cachedPackageLock -Algorithm SHA256).Hash
|
||||
$lockMatches = ($currentLockHash -eq $cachedLockHash)
|
||||
}
|
||||
|
||||
if (($currentHash -eq $cachedHash) -and $lockMatches) {
|
||||
Write-Step "Reusing cached node_modules (dependencies unchanged)..."
|
||||
Copy-Item -Path $cachedNodeModules -Destination $tempBuildDir -Recurse -Force
|
||||
$needsInstall = $false
|
||||
Write-Success "Dependencies restored from cache (saved 2-5 minutes!)"
|
||||
} else {
|
||||
Write-Info "Dependencies changed, will reinstall"
|
||||
}
|
||||
}
|
||||
|
||||
if ($needsInstall) {
|
||||
# Install dependencies (including devDependencies for Vite)
|
||||
Write-Step "Installing npm dependencies (Windows binaries)..."
|
||||
# Clear NODE_ENV to ensure devDependencies (vite, etc.) are installed
|
||||
Remove-Item Env:\NODE_ENV -ErrorAction SilentlyContinue
|
||||
npm install | Out-Default
|
||||
|
||||
$nodeModulesPath = Join-Path $tempBuildDir "node_modules"
|
||||
if (-not (Test-Path $nodeModulesPath)) {
|
||||
throw "npm install failed: node_modules not created"
|
||||
}
|
||||
Write-Success "Dependencies installed in temp"
|
||||
|
||||
# Update cache
|
||||
Write-Step "Caching node_modules for future builds..."
|
||||
if (Test-Path $cachedNodeModules) {
|
||||
Remove-Item -Path $cachedNodeModules -Recurse -Force
|
||||
}
|
||||
Copy-Item -Path $nodeModulesPath -Destination $cachedNodeModules -Recurse -Force
|
||||
Copy-Item -Path $currentPackageJson -Destination $cachedPackageJson -Force
|
||||
if (Test-Path $currentPackageLock) {
|
||||
Copy-Item -Path $currentPackageLock -Destination $cachedPackageLock -Force
|
||||
}
|
||||
Write-Success "Cache updated for next build"
|
||||
}
|
||||
Write-Success "Dependencies installed in temp"
|
||||
|
||||
# Verify Vite is installed
|
||||
$nodeModulesPath = Join-Path $tempBuildDir "node_modules"
|
||||
$vitePath = Join-Path $nodeModulesPath ".bin\vite.cmd"
|
||||
if (-not (Test-Path $vitePath)) {
|
||||
throw "Vite not found in node_modules - devDependencies not installed"
|
||||
@@ -376,6 +487,16 @@ function Copy-BackendFiles {
|
||||
$backendFiles = Get-ChildItem -Path $DestPath -Recurse -File
|
||||
Write-Success "Copied $(($backendFiles).Count) backend files"
|
||||
|
||||
# Copy .env.example explicitly (excluded from recursive copy)
|
||||
$sourceEnvExample = Join-Path $SourcePath ".env.example"
|
||||
$destEnvExample = Join-Path $DestPath ".env.example"
|
||||
if (Test-Path $sourceEnvExample) {
|
||||
Copy-Item -Path $sourceEnvExample -Destination $destEnvExample -Force
|
||||
Write-Success ".env.example template copied"
|
||||
} else {
|
||||
Write-Warning ".env.example not found in source - manual configuration required"
|
||||
}
|
||||
|
||||
# Verify requirements.txt
|
||||
$requirementsTxt = Join-Path $DestPath "requirements.txt"
|
||||
if (-not (Test-Path $requirementsTxt)) {
|
||||
@@ -464,28 +585,15 @@ function Copy-TelegramBotFiles {
|
||||
Write-Success "requirements.txt copied"
|
||||
}
|
||||
|
||||
# Create .env.example
|
||||
$envTemplate = @"
|
||||
# ROA2WEB Telegram Bot - Production Configuration
|
||||
TELEGRAM_BOT_TOKEN=your_bot_token_from_BotFather
|
||||
CLAUDE_API_KEY=your_claude_api_key
|
||||
BACKEND_URL=http://localhost:8000
|
||||
BACKEND_TIMEOUT=30
|
||||
SQLITE_DB_PATH=C:\inetpub\wwwroot\roa2web\telegram-bot\data\telegram_bot.db
|
||||
INTERNAL_API_HOST=127.0.0.1
|
||||
INTERNAL_API_PORT=8002
|
||||
LOG_LEVEL=INFO
|
||||
LOG_FILE=C:\inetpub\wwwroot\roa2web\telegram-bot\logs\bot.log
|
||||
ENVIRONMENT=production
|
||||
AUTH_CODE_EXPIRY_MINUTES=15
|
||||
JWT_REFRESH_THRESHOLD_MINUTES=5
|
||||
SESSION_TIMEOUT_MINUTES=60
|
||||
MAX_CONVERSATION_HISTORY=20
|
||||
"@
|
||||
|
||||
$envPath = Join-Path $DestPath ".env.example"
|
||||
Set-Content -Path $envPath -Value $envTemplate -Encoding UTF8
|
||||
Write-Success ".env.example template created"
|
||||
# Copy .env.example from source (keeps it synchronized with development)
|
||||
$sourceEnvExample = Join-Path $SourcePath ".env.example"
|
||||
$destEnvExample = Join-Path $DestPath ".env.example"
|
||||
if (Test-Path $sourceEnvExample) {
|
||||
Copy-Item -Path $sourceEnvExample -Destination $destEnvExample -Force
|
||||
Write-Success ".env.example template copied"
|
||||
} else {
|
||||
Write-Warning ".env.example not found in source - manual configuration required"
|
||||
}
|
||||
}
|
||||
|
||||
function Copy-SharedModules {
|
||||
@@ -552,11 +660,9 @@ function Copy-DeploymentScripts {
|
||||
|
||||
# Essential scripts for all deployments
|
||||
$scripts = @(
|
||||
"ROA2WEB-Console.ps1", # Unified deployment & management console
|
||||
"Install-ROA2WEB.ps1",
|
||||
"Install-TelegramBot.ps1",
|
||||
"Deploy-ROA2WEB.ps1",
|
||||
"Deploy-TelegramBot.ps1",
|
||||
"Manage-ROA2WEB.ps1"
|
||||
"Install-TelegramBot.ps1"
|
||||
)
|
||||
|
||||
# Add utility scripts for complete deployments
|
||||
@@ -637,11 +743,13 @@ CONTENTS:
|
||||
|
||||
DEPLOYMENT SCRIPTS:
|
||||
-------------------
|
||||
ROA2WEB-Console.ps1 Unified deployment & management console
|
||||
- Deploy components (Backend/Frontend/TelegramBot)
|
||||
- Manage services (Start/Stop/Restart)
|
||||
- Check system status and health
|
||||
|
||||
Install-ROA2WEB.ps1 First-time backend + frontend setup
|
||||
Install-TelegramBot.ps1 First-time Telegram bot setup
|
||||
Deploy-ROA2WEB.ps1 Update backend + frontend
|
||||
Deploy-TelegramBot.ps1 Update Telegram bot
|
||||
Manage-ROA2WEB.ps1 Unified service management (start/stop/restart)
|
||||
"@
|
||||
|
||||
if ($ComponentType -eq "All" -or $ComponentType -eq "TelegramBot") {
|
||||
@@ -653,6 +761,13 @@ DEPLOYMENT SCRIPTS:
|
||||
"@
|
||||
}
|
||||
|
||||
if ($ComponentType -eq "All" -or $ComponentType -eq "Frontend") {
|
||||
$readme += @"
|
||||
|
||||
Enable-HTTPS.ps1 Configure HTTPS/SSL certificates
|
||||
"@
|
||||
}
|
||||
|
||||
$readme += @"
|
||||
|
||||
|
||||
@@ -674,8 +789,9 @@ DEPLOYMENT WORKFLOW
|
||||
2. Configure environment:
|
||||
notepad C:\inetpub\wwwroot\roa2web\backend\.env
|
||||
|
||||
3. Start services:
|
||||
.\Manage-ROA2WEB.ps1 -Action Start -Component Backend
|
||||
3. Start services using the unified console:
|
||||
.\ROA2WEB-Console.ps1
|
||||
(Select: Manage Services > Start All)
|
||||
"@
|
||||
}
|
||||
|
||||
@@ -690,34 +806,44 @@ DEPLOYMENT WORKFLOW
|
||||
notepad C:\inetpub\wwwroot\roa2web\telegram-bot\.env
|
||||
|
||||
6. Start Telegram bot:
|
||||
.\Manage-ROA2WEB.ps1 -Action Start -Component TelegramBot
|
||||
.\ROA2WEB-Console.ps1
|
||||
(Select: Manage Services > Start Telegram Bot)
|
||||
"@
|
||||
}
|
||||
|
||||
$readme += @"
|
||||
|
||||
|
||||
>> UPDATES:
|
||||
-----------
|
||||
>> UPDATES (Interactive Console):
|
||||
----------------------------------
|
||||
cd scripts
|
||||
.\Manage-ROA2WEB.ps1 -Action Stop
|
||||
.\Deploy-ROA2WEB.ps1 # Updates backend + frontend
|
||||
.\Deploy-TelegramBot.ps1 # Updates Telegram bot
|
||||
.\Manage-ROA2WEB.ps1 -Action Start
|
||||
.\ROA2WEB-Console.ps1
|
||||
(Select: Deploy Components > choose what to update)
|
||||
|
||||
>> SERVICE MANAGEMENT:
|
||||
----------------------
|
||||
>> UPDATES (Command Line):
|
||||
---------------------------
|
||||
.\ROA2WEB-Console.ps1 -NonInteractive -Action DeployBackend # Update backend + frontend
|
||||
.\ROA2WEB-Console.ps1 -NonInteractive -Action DeployTelegramBot # Update Telegram bot
|
||||
.\ROA2WEB-Console.ps1 -NonInteractive -Action DeployAll # Update everything
|
||||
|
||||
>> SERVICE MANAGEMENT (Interactive):
|
||||
-------------------------------------
|
||||
.\ROA2WEB-Console.ps1
|
||||
(Select: Manage Services > choose action)
|
||||
|
||||
>> SERVICE MANAGEMENT (Command Line):
|
||||
--------------------------------------
|
||||
# Start all services
|
||||
.\Manage-ROA2WEB.ps1 -Action Start
|
||||
.\ROA2WEB-Console.ps1 -NonInteractive -Action StartAll
|
||||
|
||||
# Stop all services
|
||||
.\Manage-ROA2WEB.ps1 -Action Stop
|
||||
.\ROA2WEB-Console.ps1 -NonInteractive -Action StopAll
|
||||
|
||||
# Restart backend only
|
||||
.\Manage-ROA2WEB.ps1 -Action Restart -Component Backend
|
||||
# Restart all services
|
||||
.\ROA2WEB-Console.ps1 -NonInteractive -Action RestartAll
|
||||
|
||||
# Check status
|
||||
.\Manage-ROA2WEB.ps1 -Action Status
|
||||
.\ROA2WEB-Console.ps1 -NonInteractive -Action Status
|
||||
|
||||
================================================================================
|
||||
REQUIREMENTS
|
||||
@@ -733,6 +859,7 @@ NOTES:
|
||||
- .env files preserved during updates
|
||||
- Automatic backup before each update
|
||||
- Default install location: C:\inetpub\wwwroot\roa2web\
|
||||
- Build cache NOT included in this package (stays on build machine)
|
||||
|
||||
TROUBLESHOOTING:
|
||||
----------------
|
||||
@@ -867,6 +994,12 @@ function New-DeploymentPackage {
|
||||
# =============================================================================
|
||||
|
||||
function Main {
|
||||
# Handle -CleanCache parameter
|
||||
if ($CleanCache) {
|
||||
Clear-BuildCache
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Show interactive menu if no component specified
|
||||
if ([string]::IsNullOrWhiteSpace($Component)) {
|
||||
$script:Component = Show-BuildMenu
|
||||
|
||||
@@ -459,7 +459,7 @@ function Show-ServiceStatus {
|
||||
|
||||
$service = Get-ServiceSafe -ServiceName $ServiceName
|
||||
|
||||
Write-Host "`n$ComponentName:" -ForegroundColor Cyan
|
||||
Write-Host "`n${ComponentName}:" -ForegroundColor Cyan
|
||||
Write-Host " Service Name: $ServiceName" -ForegroundColor Gray
|
||||
|
||||
if ($service) {
|
||||
@@ -633,6 +633,14 @@ function Update-BackendFiles {
|
||||
|
||||
Write-Success "Backend files updated"
|
||||
|
||||
# Copy .env.example explicitly (excluded from recursive copy)
|
||||
$sourceEnvExample = Join-Path $sourceBackend ".env.example"
|
||||
$destEnvExample = Join-Path $Config.BackendPath ".env.example"
|
||||
if (Test-Path $sourceEnvExample) {
|
||||
Copy-Item -Path $sourceEnvExample -Destination $destEnvExample -Force
|
||||
Write-Success ".env.example template updated"
|
||||
}
|
||||
|
||||
# Check if requirements.txt changed
|
||||
$sourceReq = Join-Path $sourceBackend "requirements.txt"
|
||||
$destReq = Join-Path $Config.BackendPath "requirements.txt"
|
||||
@@ -743,12 +751,19 @@ function Update-TelegramBotFiles {
|
||||
}
|
||||
}
|
||||
|
||||
# Preserve .env file
|
||||
# Copy .env.example template (always update to keep in sync)
|
||||
$sourceEnvExample = Join-Path $Config.SourcePath ".env.example"
|
||||
$destEnvExample = Join-Path $Config.TelegramBotPath ".env.example"
|
||||
if (Test-Path $sourceEnvExample) {
|
||||
Copy-Item -Path $sourceEnvExample -Destination $destEnvExample -Force
|
||||
Write-Success ".env.example template updated"
|
||||
}
|
||||
|
||||
# Preserve .env file (or create from .env.example if missing)
|
||||
$envFile = Join-Path $Config.TelegramBotPath ".env"
|
||||
if (-not (Test-Path $envFile)) {
|
||||
$sourceEnv = Join-Path $Config.SourcePath ".env.example"
|
||||
if (Test-Path $sourceEnv) {
|
||||
Copy-Item -Path $sourceEnv -Destination $envFile -Force
|
||||
if (Test-Path $sourceEnvExample) {
|
||||
Copy-Item -Path $sourceEnvExample -Destination $envFile -Force
|
||||
Write-Warning "Created .env from .env.example - PLEASE CONFIGURE"
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user