Files
roa2web-service-auto/deployment/windows/scripts/Check-And-Deploy.ps1
Marius Mutu 3295f60faa feat: Improve Windows deployment and fix production paths
Data Entry App:
- Fix shared path finding for both dev and production environments
- Add base URL support for IIS subdirectory deployment (/data-entry/)
- Use import.meta.env.BASE_URL in router for correct path handling
- Add email-validator and python-jose dependencies

Deployment Scripts:
- Enhance Build-ROA2WEB.ps1 with improved build process
- Update ROA2WEB-Console.ps1 with Data Entry support
- Improve Publish-And-Deploy.ps1 deployment workflow
- Update deploy-config.json with new settings

Gitignore:
- Add more build cache patterns to ignore
- Add temp frontend build directories

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 19:44:15 +02:00

544 lines
17 KiB
PowerShell

<#
.SYNOPSIS
ROA2WEB - Auto-Deploy Monitor (Server-Side)
.DESCRIPTION
Server-side script that monitors C:\Temp\ for new deployment packages
and automatically deploys them using ROA2WEB-Console.ps1.
Can run:
- Via Scheduled Task (automated, silent)
- Interactive mode (manual execution with menu)
- Non-interactive mode (command-line automation)
.PARAMETER Interactive
Run in interactive mode with menu
.PARAMETER WatchPath
Path to monitor for deployment packages (default: C:\Temp)
.PARAMETER StateFile
Path to state file tracking last deployment (default: C:\Temp\ROA2WEB-Scripts\last-deploy.json)
.PARAMETER ConsoleScriptPath
Path to ROA2WEB-Console.ps1 script
.PARAMETER LogPath
Path to log file directory (default: C:\Temp\ROA2WEB-Scripts\Logs)
.PARAMETER CheckOnly
Check for updates without deploying
.EXAMPLE
.\Check-And-Deploy.ps1
Check for new packages and deploy automatically (silent)
.EXAMPLE
.\Check-And-Deploy.ps1 -Interactive
Show interactive menu with deployment options
.EXAMPLE
.\Check-And-Deploy.ps1 -CheckOnly
Check for new packages without deploying
.NOTES
Author: ROA2WEB Team
Version: 1.0 (Auto-Deploy Monitor)
Requires: Administrator privileges, PowerShell 5.1+
Designed to run via Scheduled Task for automated deployments.
#>
[CmdletBinding()]
param(
[switch]$Interactive,
[string]$WatchPath = "C:\Temp",
[string]$StateFile = "C:\Temp\ROA2WEB-Scripts\last-deploy.json",
[string]$ConsoleScriptPath = "",
[string]$LogPath = "C:\Temp\ROA2WEB-Scripts\Logs",
[switch]$CheckOnly
)
$ErrorActionPreference = "Stop"
# =============================================================================
# CONFIGURATION
# =============================================================================
$script:Config = @{
WatchPath = $WatchPath
StateFile = $StateFile
LogPath = $LogPath
ConsoleScriptPath = if ($ConsoleScriptPath) {
$ConsoleScriptPath
} else {
# Auto-detect ROA2WEB-Console.ps1
$possiblePaths = @(
"C:\inetpub\wwwroot\roa2web\scripts\ROA2WEB-Console.ps1",
(Join-Path $PSScriptRoot "ROA2WEB-Console.ps1")
)
$found = $possiblePaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($found) { $found } else { "" }
}
}
# =============================================================================
# HELPER FUNCTIONS
# =============================================================================
function Write-Log {
param(
[string]$Message,
[ValidateSet("INFO", "SUCCESS", "WARNING", "ERROR")]
[string]$Level = "INFO"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] [$Level] $Message"
# Ensure log directory exists
if (-not (Test-Path $script:Config.LogPath)) {
New-Item -ItemType Directory -Path $script:Config.LogPath -Force | Out-Null
}
$logFile = Join-Path $script:Config.LogPath "check-and-deploy.log"
Add-Content -Path $logFile -Value $logMessage
# Also write to console if interactive
if ($Interactive) {
$color = switch ($Level) {
"SUCCESS" { "Green" }
"WARNING" { "Yellow" }
"ERROR" { "Red" }
default { "White" }
}
Write-Host $logMessage -ForegroundColor $color
}
}
function Write-Step {
param([string]$Message)
Write-Host "`n[*] $Message" -ForegroundColor Cyan
Write-Log -Message $Message -Level "INFO"
}
function Write-Success {
param([string]$Message)
Write-Host " [OK] $Message" -ForegroundColor Green
Write-Log -Message $Message -Level "SUCCESS"
}
function Write-Error {
param([string]$Message)
Write-Host " [ERROR] $Message" -ForegroundColor Red
Write-Log -Message $Message -Level "ERROR"
}
function Write-Warning {
param([string]$Message)
Write-Host " [WARN] $Message" -ForegroundColor Yellow
Write-Log -Message $Message -Level "WARNING"
}
function Wait-ForKeyPress {
Write-Host "`nPress any key to continue..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
# =============================================================================
# STATE MANAGEMENT
# =============================================================================
function Get-DeploymentState {
if (Test-Path $script:Config.StateFile) {
try {
return Get-Content -Path $script:Config.StateFile -Raw | ConvertFrom-Json
} catch {
Write-Warning "Failed to load state file, starting fresh"
return $null
}
}
return $null
}
function Save-DeploymentState {
param(
[Parameter(Mandatory)]
[hashtable]$DeploymentInfo
)
try {
# Load existing state
$state = Get-DeploymentState
$newEntry = @{
packageName = $DeploymentInfo.PackageName
component = $DeploymentInfo.Component
timestamp = (Get-Date).ToUniversalTime().ToString("o")
status = $DeploymentInfo.Status
services = $DeploymentInfo.Services
}
if ($state) {
# Add current deployment to history
if (-not $state.history) {
$state.history = @()
}
if ($state.lastDeployment) {
$state.history = @($state.lastDeployment) + $state.history
}
# Keep only last 10 entries in history
if ($state.history.Count -gt 10) {
$state.history = $state.history | Select-Object -First 10
}
$state.lastDeployment = $newEntry
} else {
$state = @{
lastDeployment = $newEntry
history = @()
}
}
# Ensure directory exists
$stateDir = Split-Path $script:Config.StateFile -Parent
if (-not (Test-Path $stateDir)) {
New-Item -ItemType Directory -Path $stateDir -Force | Out-Null
}
# Save state
$state | ConvertTo-Json -Depth 10 | Set-Content -Path $script:Config.StateFile
Write-Success "Deployment state saved"
} catch {
Write-Error "Failed to save deployment state: $_"
}
}
# =============================================================================
# PACKAGE DETECTION
# =============================================================================
function Get-LatestDeploymentPackage {
Write-Step "Scanning for deployment packages..."
if (-not (Test-Path $script:Config.WatchPath)) {
Write-Warning "Watch path does not exist: $($script:Config.WatchPath)"
return $null
}
# Find all deploy-* folders
$packages = Get-ChildItem -Path $script:Config.WatchPath -Directory |
Where-Object { $_.Name -match "^deploy-\d{8}-\d{6}$" } |
Sort-Object LastWriteTime -Descending
if ($packages.Count -eq 0) {
Write-Warning "No deployment packages found"
return $null
}
$latest = $packages | Select-Object -First 1
Write-Success "Found $($packages.Count) package(s), latest: $($latest.Name)"
return $latest
}
function Test-IsNewPackage {
param([Parameter(Mandatory)]$Package)
$state = Get-DeploymentState
if (-not $state -or -not $state.lastDeployment) {
Write-Success "No previous deployment found - this is a new package"
return $true
}
$lastDeployed = $state.lastDeployment.packageName
if ($Package.Name -ne $lastDeployed) {
Write-Success "New package detected (last deployed: $lastDeployed)"
return $true
}
Write-Log -Message "Package already deployed: $($Package.Name)" -Level "INFO"
return $false
}
# =============================================================================
# DEPLOYMENT EXECUTION
# =============================================================================
function Invoke-Deployment {
param([Parameter(Mandatory)]$Package)
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " STARTING DEPLOYMENT" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host "`nPackage: $($Package.Name)" -ForegroundColor Yellow
Write-Host "Path: $($Package.FullName)" -ForegroundColor Gray
# Find ROA2WEB-Console.ps1 in package
$consoleScript = Join-Path $Package.FullName "scripts\ROA2WEB-Console.ps1"
if (-not (Test-Path $consoleScript)) {
Write-Error "ROA2WEB-Console.ps1 not found in package: $consoleScript"
return $false
}
try {
Write-Step "Executing deployment via ROA2WEB-Console.ps1..."
# Set environment variable to tell ROA2WEB-Console where the package is
$env:ROA2WEB_SOURCE = $Package.FullName
# Execute deployment (deploy all components)
Push-Location (Split-Path $consoleScript -Parent)
& $consoleScript -NonInteractive -Action DeployAll
# Capture exit code IMMEDIATELY (before any other command that might reset it)
$exitCode = $LASTEXITCODE
Pop-Location
# Check if exit code indicates success (0 = success)
$deploySuccess = ($exitCode -eq 0)
if ($deploySuccess) {
Write-Success "Deployment completed successfully (exit code: $exitCode)"
# Save deployment state
Save-DeploymentState -DeploymentInfo @{
PackageName = $Package.Name
Component = "All"
Status = "Success"
Services = @{
backend = "Running"
telegramBot = "Running"
dataEntry = "Running"
}
}
Write-Host "`n" + ("=" * 70) -ForegroundColor Green
Write-Host " DEPLOYMENT SUCCESSFUL" -ForegroundColor Green
Write-Host ("=" * 70) -ForegroundColor Green
return $true
} else {
Write-Error "Deployment failed (exit code: $exitCode)"
# Save failure state
Save-DeploymentState -DeploymentInfo @{
PackageName = $Package.Name
Component = "All"
Status = "Failed"
Services = @{}
}
Write-Host "`n" + ("=" * 70) -ForegroundColor Red
Write-Host " DEPLOYMENT FAILED" -ForegroundColor Red
Write-Host ("=" * 70) -ForegroundColor Red
return $false
}
} catch {
Write-Error "Deployment exception: $_"
Write-Log -Message $_.ScriptStackTrace -Level "ERROR"
# Save failure state
Save-DeploymentState -DeploymentInfo @{
PackageName = $Package.Name
Component = "All"
Status = "Failed"
Services = @{}
}
return $false
}
}
# =============================================================================
# INTERACTIVE MENU
# =============================================================================
function Show-MainMenu {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " ROA2WEB - Auto-Deploy Monitor (Interactive)" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " [1] Check for Updates (No Deploy)" -ForegroundColor White
Write-Host " (Scan for new packages without deploying)" -ForegroundColor Gray
Write-Host ""
Write-Host " [2] Check and Deploy Now" -ForegroundColor White
Write-Host " (Find latest package and deploy if new)" -ForegroundColor Gray
Write-Host ""
Write-Host " [3] View Deployment History" -ForegroundColor White
Write-Host " (Show previous deployments)" -ForegroundColor Gray
Write-Host ""
Write-Host " [4] View Current Configuration" -ForegroundColor White
Write-Host " (Display monitor settings)" -ForegroundColor Gray
Write-Host ""
Write-Host " [Q] Quit" -ForegroundColor Red
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Cyan
do {
Write-Host "`nYour choice: " -ForegroundColor Yellow -NoNewline
$choice = Read-Host
switch ($choice.ToUpper()) {
"1" { return "CheckOnly" }
"2" { return "CheckAndDeploy" }
"3" { return "History" }
"4" { return "ViewConfig" }
"Q" { return "Quit" }
default {
Write-Host "Invalid choice. Please select 1-4 or Q." -ForegroundColor Red
}
}
} while ($true)
}
function Show-DeploymentHistory {
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Deployment History" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
$state = Get-DeploymentState
if (-not $state) {
Write-Host "`nNo deployment history found" -ForegroundColor Yellow
return
}
if ($state.lastDeployment) {
Write-Host "`nLast Deployment:" -ForegroundColor Yellow
Write-Host " Package: $($state.lastDeployment.packageName)" -ForegroundColor Gray
Write-Host " Component: $($state.lastDeployment.component)" -ForegroundColor Gray
Write-Host " Timestamp: $($state.lastDeployment.timestamp)" -ForegroundColor Gray
Write-Host " Status: $($state.lastDeployment.status)" -ForegroundColor $(if ($state.lastDeployment.status -eq "Success") { "Green" } else { "Red" })
}
if ($state.history -and $state.history.Count -gt 0) {
Write-Host "`nPrevious Deployments:" -ForegroundColor Yellow
$i = 1
foreach ($entry in $state.history) {
Write-Host " [$i] $($entry.packageName) - $($entry.timestamp) - $($entry.status)" -ForegroundColor Gray
$i++
}
}
Write-Host ("=" * 70) -ForegroundColor Cyan
}
function Show-Configuration {
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Current Configuration" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " Watch Path: $($script:Config.WatchPath)" -ForegroundColor Gray
Write-Host " State File: $($script:Config.StateFile)" -ForegroundColor Gray
Write-Host " Log Path: $($script:Config.LogPath)" -ForegroundColor Gray
Write-Host " Console Script: $($script:Config.ConsoleScriptPath)" -ForegroundColor Gray
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Cyan
}
# =============================================================================
# MAIN CHECK & DEPLOY LOGIC
# =============================================================================
function Invoke-CheckAndDeploy {
param([switch]$CheckOnly)
Write-Log -Message "=== Check and Deploy Started ===" -Level "INFO"
# Get latest package
$latestPackage = Get-LatestDeploymentPackage
if (-not $latestPackage) {
Write-Log -Message "No deployment packages found" -Level "INFO"
return $false
}
# Check if it's a new package
$isNew = Test-IsNewPackage -Package $latestPackage
if (-not $isNew) {
Write-Log -Message "No new packages to deploy" -Level "INFO"
return $false
}
if ($CheckOnly) {
Write-Success "New package available: $($latestPackage.Name)"
Write-Host "`nRun without -CheckOnly to deploy" -ForegroundColor Yellow
return $false
}
# Deploy the package
$deploySuccess = Invoke-Deployment -Package $latestPackage
Write-Log -Message "=== Check and Deploy Completed (Success: $deploySuccess) ===" -Level "INFO"
return $deploySuccess
}
# =============================================================================
# MAIN EXECUTION FLOW
# =============================================================================
function Main {
# Check if running as Administrator
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
$isAdmin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Host "`n[ERROR] This script requires Administrator privileges" -ForegroundColor Red
Write-Host "Please run PowerShell as Administrator and try again.`n" -ForegroundColor Yellow
exit 1
}
# Interactive mode
if ($Interactive) {
do {
$mainChoice = Show-MainMenu
switch ($mainChoice) {
"CheckOnly" {
Invoke-CheckAndDeploy -CheckOnly
Wait-ForKeyPress
}
"CheckAndDeploy" {
Invoke-CheckAndDeploy
Wait-ForKeyPress
}
"History" {
Show-DeploymentHistory
Wait-ForKeyPress
}
"ViewConfig" {
Show-Configuration
Wait-ForKeyPress
}
"Quit" {
Write-Host "`nGoodbye!`n" -ForegroundColor Cyan
return
}
}
} while ($true)
}
# Non-interactive mode (for Scheduled Task)
else {
Invoke-CheckAndDeploy -CheckOnly:$CheckOnly | Out-Null
}
}
# Run main
Main