Add deployment automation system with build/publish workflow and fix path resolution

This commit introduces a complete deployment automation system for Windows Server deployment:

New Features:
- Publish-And-Deploy.ps1: Interactive console for building locally and transferring to server
  * Supports auto-detection of transfer method (Windows Share or SSH)
  * Configurable via deploy-config.json
  * Integrated with Build-ROA2WEB.ps1 for consistent builds
- Check-And-Deploy.ps1: Server-side auto-deployment script
  * Monitors transfer directory for new packages
  * Automatically deploys when new version detected
  * Integrates with ROA2WEB-Console.ps1 for deployment
- Setup-AutoDeploy.ps1: Configures scheduled task for auto-deployment
- DEPLOYMENT_AUTOMATION.md: Complete documentation for automation workflow

Bug Fixes:
- Fix path resolution in Publish-And-Deploy.ps1: Added Resolve-FullPath function to correctly resolve ../deploy-package path
- Fix ROA2WEB-Console.ps1 exit codes: Properly return boolean values and exit codes in non-interactive mode
- Fix Build-ROA2WEB.ps1 script copying: Added debug output and force deletion to avoid caching issues

Enhancements:
- ROA2WEB-Console.ps1: Support for ROA2WEB_SOURCE environment variable for flexible source paths
- Build-ROA2WEB.ps1: Enhanced debug output for troubleshooting script copying issues

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-14 14:59:47 +02:00
parent 914ab0107e
commit 87859e5510
7 changed files with 2714 additions and 14 deletions

View File

@@ -0,0 +1,751 @@
# ROA2WEB - Automated Deployment System
## 📋 Overview
This document describes the **automated deployment system** for ROA2WEB Windows Server deployment. The system enables:
-**Build locally** on dev machine (WSL/Windows)
-**Auto-transfer** via Windows Share (LAN) or SSH (remote)
-**Auto-deploy** on server (no manual intervention needed)
-**Interactive menus** for easy configuration
-**No Remote Desktop required**
---
## 🏗️ Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Dev Machine (WSL/Windows) │
│ │
│ Publish-And-Deploy.ps1 (Interactive Menu) │
│ ├─ Load deploy-config.json │
│ ├─ Build-ROA2WEB.ps1 (create deploy-package/) │
│ └─ Transfer: │
│ ├─ Auto: Try Windows Share → fallback SSH │
│ ├─ Windows Share: \\10.0.20.36\ROA2WEB-Deploy │
│ └─ SSH: scp to 10.0.20.36:22122 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Windows Server │
│ │
│ C:\Temp\ │
│ └─ deploy-20241112-153045\ │
│ ├─ backend/ │
│ ├─ frontend/ │
│ ├─ telegram-bot/ │
│ └─ scripts/ROA2WEB-Console.ps1 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Scheduled Task: ROA2WEB-AutoDeploy (every 5 minutes) │
│ │
│ Check-And-Deploy.ps1 │
│ ├─ Scan C:\Temp\ for deploy-* folders │
│ ├─ Find newest package (by LastWriteTime) │
│ ├─ Compare with last-deploy.json │
│ └─ If newer: │
│ ├─ Set $env:ROA2WEB_SOURCE = package path │
│ ├─ Execute ROA2WEB-Console.ps1 -Action DeployAll │
│ └─ Update last-deploy.json │
└─────────────────────────────────────────────────────────────────┘
```
---
## 🚀 Quick Start Guide
### Dev Machine Setup (One-Time)
1. **Edit Configuration** (optional, defaults are provided):
```powershell
cd deployment/windows/scripts
notepad deploy-config.json
```
Default configuration:
```json
{
"server": {
"host": "10.0.20.36",
"sshPort": 22122,
"sshUser": "Administrator",
"sshKeyPath": "~\\.ssh\\id_ed25519",
"windowsShare": "\\\\10.0.20.36\\Temp"
}
}
```
2. **Test Connections**:
```powershell
.\Publish-And-Deploy.ps1
# Menu: [2] Test Connections
```
---
### Server Setup (One-Time)
1. **Windows Share** (already exists):
```powershell
# Share already exists: \\10.0.20.36\Temp
# Maps to: C:\Temp on server
# Create scripts directory (if not exists)
New-Item -Path "C:\Temp\ROA2WEB-Scripts" -ItemType Directory -Force
```
2. **Run Setup Wizard**:
```powershell
# Copy Setup-AutoDeploy.ps1 to server, then:
cd C:\path\to\scripts
.\Setup-AutoDeploy.ps1
```
The wizard will:
- ✅ Create directory structure (`C:\Temp`, `C:\Temp\ROA2WEB-Scripts`)
- ✅ Copy `Check-And-Deploy.ps1` to scripts folder
- ✅ Create Scheduled Task (`ROA2WEB-AutoDeploy`)
- ✅ Configure auto-deploy (every 5 minutes)
---
## 📖 Usage
### Interactive Deployment (Dev Machine)
```powershell
cd deployment/windows/scripts
.\Publish-And-Deploy.ps1
```
**Main Menu:**
```
[1] Build & Publish Package
→ Select Component (All/Frontend/Backend/TelegramBot)
→ Select Transfer Method (Auto/WindowsShare/SSH)
→ Build + Transfer automatically
[2] Test Connections
→ Test Windows Share accessibility
→ Test SSH connection
[3] Configure Settings
→ Edit server host, SSH port, paths, etc.
[4] View Current Configuration
→ Display active settings
[Q] Quit
```
---
### Non-Interactive Deployment (Dev Machine)
```powershell
# Quick deploy (auto-detect transfer method)
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component All
# Force Windows Share (LAN)
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component All -TransferMethod WindowsShare
# Force SSH (remote/WAN)
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component Frontend -TransferMethod SSH
# Test connections only
.\Publish-And-Deploy.ps1 -NonInteractive -Action TestConnections
```
---
### Server-Side Management
#### Manual Check & Deploy (Server)
```powershell
cd C:\ROA2WEB-Scripts
# Interactive menu
.\Check-And-Deploy.ps1 -Interactive
# Non-interactive check + deploy
.\Check-And-Deploy.ps1
# Check only (no deploy)
.\Check-And-Deploy.ps1 -CheckOnly
```
#### Manage Scheduled Task (Server)
```powershell
# View task
Get-ScheduledTask -TaskName "ROA2WEB-AutoDeploy"
# Start task manually (trigger check now)
Start-ScheduledTask -TaskName "ROA2WEB-AutoDeploy"
# Disable auto-deploy temporarily
Disable-ScheduledTask -TaskName "ROA2WEB-AutoDeploy"
# Re-enable auto-deploy
Enable-ScheduledTask -TaskName "ROA2WEB-AutoDeploy"
# View logs
Get-Content "C:\ROA2WEB-Scripts\Logs\check-and-deploy.log" -Tail 50
```
---
## 📁 File Structure
### Dev Machine
```
deployment/windows/scripts/
├── deploy-config.json # Configuration file (edit server settings)
├── Publish-And-Deploy.ps1 # Interactive build & publish script
├── Build-ROA2WEB.ps1 # Existing build script (unchanged)
├── ROA2WEB-Console.ps1 # Existing deployment script (updated)
└── Setup-AutoDeploy.ps1 # Server setup wizard
```
### Server
```
C:\Temp\ # Monitored folder (Windows Share)
├── ROA2WEB-Scripts\ # Scripts and state (permanent)
│ ├── Check-And-Deploy.ps1 # Monitoring script (runs via scheduled task)
│ ├── Setup-AutoDeploy.ps1 # Setup wizard (optional, for debug)
│ ├── last-deploy.json # Tracks last deployed package
│ └── Logs\
│ └── check-and-deploy.log # Deployment logs
├── deploy-20241112-153045\ # Timestamped deployment packages
│ ├── backend/
│ ├── frontend/
│ ├── telegram-bot/
│ └── scripts/
│ └── ROA2WEB-Console.ps1
└── deploy-20241111-120000\ # Previous package (kept as backup)
```
---
## 🔧 Configuration Reference
### deploy-config.json
```json
{
"server": {
"host": "10.0.20.36", // Server IP/hostname
"sshPort": 22122, // SSH port
"sshUser": "Administrator", // SSH username
"sshKeyPath": "~\\.ssh\\id_ed25519", // SSH private key path
"windowsShare": "\\\\10.0.20.36\\Temp", // Network share path
"deployPath": "C:\\Temp" // Server-side deploy path
},
"build": {
"defaultComponent": "All", // Default: All/Frontend/Backend/TelegramBot
"outputPath": "./deploy-package" // Local build output
},
"transfer": {
"defaultMethod": "Auto", // Auto/WindowsShare/SSH
"scpRemotePath": "C:/Temp", // SCP remote path (Windows format)
"retryAttempts": 3, // Transfer retry attempts
"timeout": 300 // Transfer timeout (seconds)
},
"deployment": {
"autoDeployEnabled": true, // Auto-deploy when new package found
"checkIntervalMinutes": 5 // How often to check (minutes)
}
}
```
**Editing Configuration:**
**Option 1:** Edit JSON file directly:
```powershell
notepad deployment/windows/scripts/deploy-config.json
```
**Option 2:** Use interactive configuration editor:
```powershell
.\Publish-And-Deploy.ps1
# Menu: [3] Configure Settings
```
---
## 🔄 Transfer Methods
### Auto-detect (Recommended)
```powershell
.\Publish-And-Deploy.ps1 -TransferMethod Auto
```
- **LAN**: Tries Windows Share first (fast, ~30 seconds)
- **Remote**: Falls back to SSH if share not accessible
- **Smart**: Automatically chooses best method
### Windows Share (LAN Only)
```powershell
.\Publish-And-Deploy.ps1 -TransferMethod WindowsShare
```
- **Speed**: ⚡⚡⚡⚡⚡ Very fast (LAN)
- **Requirements**: Dev machine in same network as server
- **Protocol**: SMB (port 445)
- **Use case**: Office/home network
### SSH/SCP (LAN or Remote)
```powershell
.\Publish-And-Deploy.ps1 -TransferMethod SSH
```
- **Speed**: ⚡⚡⚡ Decent (depends on bandwidth)
- **Requirements**: SSH key configured, server accessible
- **Protocol**: SSH (port 22122)
- **Use case**: Remote work, VPN, internet
---
## 🛠️ Troubleshooting
### Dev Machine Issues
#### Error: "Windows Share is not accessible"
```powershell
# Test share access
Test-Path "\\10.0.20.36\Temp"
# Check network connectivity
Test-NetConnection -ComputerName 10.0.20.36 -Port 445
# Solution: Use SSH instead
.\Publish-And-Deploy.ps1 -TransferMethod SSH
```
#### Error: "SSH connection failed"
```powershell
# Test SSH manually
ssh -i ~\.ssh\id_ed25519 -p 22122 Administrator@10.0.20.36
# Common issues:
# 1. Wrong key path → Edit deploy-config.json
# 2. Key permissions → Ensure key file has correct Windows permissions
# 3. Server not reachable → Check firewall/VPN
```
#### Error: "Configuration file not found"
```powershell
# Script will auto-create default config on first run
.\Publish-And-Deploy.ps1
# Or create manually from example:
Copy-Item deploy-config.json.example deploy-config.json
notepad deploy-config.json
```
---
### Server Issues
#### Scheduled Task Not Running
```powershell
# Check task status
Get-ScheduledTask -TaskName "ROA2WEB-AutoDeploy" | Select-Object State, LastRunTime, LastTaskResult
# View task history
Get-ScheduledTaskInfo -TaskName "ROA2WEB-AutoDeploy"
# Check logs
Get-Content "C:\ROA2WEB-Scripts\Logs\check-and-deploy.log" -Tail 50
# Solution: Run setup wizard again
.\Setup-AutoDeploy.ps1
```
#### Deployment Not Triggered
```powershell
# Check if new package exists
Get-ChildItem "C:\Temp" -Directory | Where-Object { $_.Name -like "deploy-*" } | Sort-Object LastWriteTime -Descending
# Check last deployment state
Get-Content "C:\Temp\ROA2WEB-Scripts\last-deploy.json" | ConvertFrom-Json
# Manual trigger
cd C:\Temp\ROA2WEB-Scripts
.\Check-And-Deploy.ps1 -Interactive
# Menu: [2] Check and Deploy Now
```
#### Package Found But Not Deployed
```powershell
# Check if package is newer than last deployment
$state = Get-Content "C:\Temp\ROA2WEB-Scripts\last-deploy.json" | ConvertFrom-Json
$latest = Get-ChildItem "C:\Temp\deploy-*" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Write-Host "Last deployed: $($state.lastDeployment.packageName)"
Write-Host "Latest package: $($latest.Name)"
# Solution: Delete state file to force redeploy
Remove-Item "C:\Temp\ROA2WEB-Scripts\last-deploy.json"
cd C:\Temp\ROA2WEB-Scripts
.\Check-And-Deploy.ps1
```
---
## 📊 Monitoring & Logs
### View Deployment Logs (Server)
```powershell
# Real-time log monitoring
Get-Content "C:\ROA2WEB-Scripts\Logs\check-and-deploy.log" -Wait
# Last 100 lines
Get-Content "C:\ROA2WEB-Scripts\Logs\check-and-deploy.log" -Tail 100
# Search for errors
Select-String -Path "C:\ROA2WEB-Scripts\Logs\check-and-deploy.log" -Pattern "ERROR"
```
### View Deployment History (Server)
```powershell
# Interactive menu
.\Check-And-Deploy.ps1 -Interactive
# Menu: [3] View Deployment History
# Or view state file directly
Get-Content "C:\ROA2WEB-Scripts\last-deploy.json" | ConvertFrom-Json | Format-List
```
### Check Service Status (Server)
```powershell
# After deployment, verify services
Get-Service -Name "ROA2WEB-Backend", "ROA2WEB-TelegramBot"
# Or use ROA2WEB-Console.ps1
cd C:\inetpub\wwwroot\roa2web\scripts
.\ROA2WEB-Console.ps1 -NonInteractive -Action Status
```
---
## 🔐 Security Considerations
### SSH Key Management
- **Never commit SSH private keys** to Git
- Store keys in secure location (`~/.ssh/` with `chmod 600`)
- Use separate key for deployment (not personal key)
- Rotate keys periodically
### Windows Share Permissions
```powershell
# Restrict share access to specific users (recommended for production)
New-SmbShare -Name "ROA2WEB-Deploy" -Path "C:\ROA2WEB-Deploy" -FullAccess "DOMAIN\DeployUser"
# Or keep "Everyone" for internal network (development)
```
### Scheduled Task Security
- Task runs as **SYSTEM** account (full privileges)
- Ensure `C:\ROA2WEB-Deploy` is on local drive (not network share)
- Review logs regularly for unauthorized deployments
---
## 🎯 Best Practices
### 1. **Test Before Production**
```powershell
# Always test connections first
.\Publish-And-Deploy.ps1 -NonInteractive -Action TestConnections
# Then test with small component
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component Backend
```
### 2. **Backup Before Deploy**
- ROA2WEB-Console.ps1 automatically creates backups
- Backups stored in `C:\inetpub\wwwroot\roa2web\backups\`
- Last 10 backups retained
### 3. **Monitor Deployments**
```powershell
# Set up email alerts for failed deployments (optional)
# Add to Check-And-Deploy.ps1:
if (-not $deploySuccess) {
Send-MailMessage -To "admin@company.com" -Subject "Deployment Failed" ...
}
```
### 4. **Regular Cleanup**
```powershell
# Clean old deployment packages (keep last 5)
Get-ChildItem "C:\ROA2WEB-Deploy\deploy-*" |
Sort-Object LastWriteTime -Descending |
Select-Object -Skip 5 |
Remove-Item -Recurse -Force
```
---
## 📚 Advanced Configuration
### Custom Check Interval
```powershell
# Edit scheduled task interval (e.g., every 10 minutes)
$trigger = Get-ScheduledTask -TaskName "ROA2WEB-AutoDeploy" | Get-ScheduledTaskTrigger
$trigger.Repetition.Interval = "PT10M"
Set-ScheduledTask -TaskName "ROA2WEB-AutoDeploy" -Trigger $trigger
```
### Deploy Specific Components Only
```powershell
# Backend only
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component Backend
# Frontend only
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component Frontend
# Telegram Bot only
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component TelegramBot
```
### Custom SSH Port Forwarding
```powershell
# If server is behind NAT/firewall, use port forwarding
# Router: Forward external port 2222 → 10.0.20.36:22122
# Edit deploy-config.json:
{
"server": {
"host": "public-ip-or-domain.com",
"sshPort": 2222,
...
}
}
```
---
## 🆘 Support
### Common Commands Reference
**Dev Machine:**
```powershell
# Quick deploy
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component All
# Test connections
.\Publish-And-Deploy.ps1 -NonInteractive -Action TestConnections
# View config
.\Publish-And-Deploy.ps1 -NonInteractive -Action ViewConfig
```
**Server:**
```powershell
# Check for updates
.\Check-And-Deploy.ps1 -CheckOnly
# Deploy now
.\Check-And-Deploy.ps1
# Interactive menu
.\Check-And-Deploy.ps1 -Interactive
# View logs
Get-Content "C:\ROA2WEB-Scripts\Logs\check-and-deploy.log" -Tail 50
```
---
## 📝 Migration from Manual Deployment
If you were previously using manual deployment (Build-ROA2WEB.ps1 + manual copy):
1. **One-time server setup:**
```powershell
# Copy Setup-AutoDeploy.ps1 to server
.\Setup-AutoDeploy.ps1
```
2. **Update workflow:**
```powershell
# OLD: Manual build + copy + Remote Desktop deploy
.\Build-ROA2WEB.ps1
# ... manual copy ...
# ... Remote Desktop to server ...
# ... run ROA2WEB-Console.ps1 ...
# NEW: One command, fully automated
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component All
# → Server auto-deploys within 5 minutes
```
3. **Benefits:**
- ⏱️ **Time saved**: 15 minutes → 2 minutes
- 🎯 **Zero manual steps**: No Remote Desktop needed
- 🔄 **Consistent**: Same process every time
- 📊 **Audit trail**: Full logs and deployment history
---
## 🧪 Testing the Complete Workflow
### End-to-End Testing Guide
After completing the setup, test the entire automated deployment workflow:
#### Step 1: Server Setup (One-Time)
```powershell
# On Dev Machine: Copy scripts to server via Windows Share
Copy-Item -Path ".\Check-And-Deploy.ps1" -Destination "\\10.0.20.36\Temp\ROA2WEB-Scripts\"
Copy-Item -Path ".\Setup-AutoDeploy.ps1" -Destination "\\10.0.20.36\Temp\ROA2WEB-Scripts\"
```
```powershell
# On Server: Run setup wizard (via Remote Desktop - only once)
cd C:\Temp\ROA2WEB-Scripts
.\Setup-AutoDeploy.ps1
# Follow wizard prompts:
# - Monitor Path: C:\Temp (default)
# - Scripts Path: C:\Temp\ROA2WEB-Scripts (default)
# - Check Interval: 5 minutes (default)
# - Auto-Deploy: Yes (default)
# - Create Scheduled Task: Yes (default)
```
#### Step 2: Test Connections (Dev Machine)
```powershell
cd /mnt/e/proiecte/roa2web/deployment/windows/scripts
# Run connection tests
.\Publish-And-Deploy.ps1
# Select: [2] Test Connections
# Expected output:
# ✅ Windows Share accessible: \\10.0.20.36\Temp
# ✅ SSH connection successful: Administrator@10.0.20.36:22122
```
#### Step 3: First Deployment Test (Dev Machine)
```powershell
# Build and publish a test package
.\Publish-And-Deploy.ps1
# Menu workflow:
# [1] Build & Publish Package
# → Component: [1] All (or test with Backend only)
# → Transfer Method: [1] Auto (recommended)
# → Confirm: Y
# Expected output:
# ✅ Building deployment package...
# ✅ Transferring via Windows Share...
# ✅ Package copied: \\10.0.20.36\Temp\deploy-20241113-HHMMSS\
# ✅ Server will auto-deploy within 5 minutes
```
#### Step 4: Verify Auto-Deploy (Server)
```powershell
# Wait 1-5 minutes, then check deployment logs on server:
Get-Content "C:\Temp\ROA2WEB-Scripts\Logs\check-and-deploy.log" -Tail 30
# Expected log entries:
# [2024-11-13 HH:MM:SS] [INFO] === Check and Deploy Started ===
# [2024-11-13 HH:MM:SS] [SUCCESS] Found 1 package(s), latest: deploy-20241113-HHMMSS
# [2024-11-13 HH:MM:SS] [SUCCESS] New package detected
# [2024-11-13 HH:MM:SS] [INFO] Executing deployment via ROA2WEB-Console.ps1...
# [2024-11-13 HH:MM:SS] [SUCCESS] Deployment completed successfully
```
```powershell
# Verify services are running:
Get-Service -Name "ROA2WEB-Backend", "ROA2WEB-TelegramBot"
# Expected output:
# Status Name DisplayName
# ------ ---- -----------
# Running ROA2WEB-Backend ROA2WEB Backend Service
# Running ROA2WEB-TelegramBot ROA2WEB Telegram Bot Service
```
#### Step 5: Verify Web Application
```
# Open browser and test:
http://10.0.20.36/ # Frontend should load
http://10.0.20.36/api/docs # Backend API docs should be accessible
```
### Manual Testing (Optional)
If you want to test without waiting for Scheduled Task:
```powershell
# On Server: Manual deployment test
cd C:\Temp\ROA2WEB-Scripts
.\Check-And-Deploy.ps1 -Interactive
# Menu workflow:
# [1] Check for Updates (No Deploy) - verify package is detected
# [2] Check and Deploy Now - force immediate deployment
# [3] View Deployment History - see deployment records
```
### Troubleshooting Failed Tests
**If Windows Share test fails:**
```powershell
# Verify network connectivity
Test-NetConnection -ComputerName 10.0.20.36 -Port 445
# Try manual access
Test-Path "\\10.0.20.36\Temp"
# Fallback: Use SSH transfer instead
.\Publish-And-Deploy.ps1 -TransferMethod SSH
```
**If SSH test fails:**
```powershell
# Test SSH manually
ssh -i ~\.ssh\id_ed25519 -p 22122 Administrator@10.0.20.36
# If key path is wrong, update config:
notepad deploy-config.json
# Update: "sshKeyPath": "correct\\path\\to\\key"
```
**If auto-deploy doesn't trigger:**
```powershell
# Verify scheduled task is running (on server):
Get-ScheduledTask -TaskName "ROA2WEB-AutoDeploy" | Select-Object State, LastRunTime
# Manually trigger task:
Start-ScheduledTask -TaskName "ROA2WEB-AutoDeploy"
# Check for errors in logs:
Select-String -Path "C:\Temp\ROA2WEB-Scripts\Logs\check-and-deploy.log" -Pattern "ERROR"
```
---
## 🎉 Success!
You've now set up the automated deployment system for ROA2WEB! Deployments are now as simple as:
```powershell
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component All
```
**Server auto-deploys within 5 minutes** - no Remote Desktop, no manual steps! 🚀

View File

@@ -658,6 +658,11 @@ function Copy-DeploymentScripts {
$scriptsDir = Join-Path $DestPath "scripts" $scriptsDir = Join-Path $DestPath "scripts"
New-Item -ItemType Directory -Path $scriptsDir -Force | Out-Null New-Item -ItemType Directory -Path $scriptsDir -Force | Out-Null
# Debug: Show paths
Write-Host " [DEBUG] ScriptsSourcePath: $ScriptsSourcePath" -ForegroundColor Magenta
Write-Host " [DEBUG] DestPath: $DestPath" -ForegroundColor Magenta
Write-Host " [DEBUG] ScriptsDir: $scriptsDir" -ForegroundColor Magenta
# Essential scripts for all deployments # Essential scripts for all deployments
$scripts = @( $scripts = @(
"ROA2WEB-Console.ps1", # Unified deployment & management console "ROA2WEB-Console.ps1", # Unified deployment & management console
@@ -684,7 +689,25 @@ function Copy-DeploymentScripts {
foreach ($script in $scripts) { foreach ($script in $scripts) {
$scriptPath = Join-Path $ScriptsSourcePath $script $scriptPath = Join-Path $ScriptsSourcePath $script
if (Test-Path $scriptPath) { if (Test-Path $scriptPath) {
$destScript = Join-Path $scriptsDir $script
# Debug: Show file info
$sourceSize = (Get-Item $scriptPath).Length
Write-Host " [DEBUG] Copying $script (size: $sourceSize bytes)" -ForegroundColor Magenta
# Force delete destination first to avoid caching issues
if (Test-Path $destScript) {
$oldSize = (Get-Item $destScript).Length
Write-Host " [DEBUG] Deleting old $script (was: $oldSize bytes)" -ForegroundColor Magenta
Remove-Item -Path $destScript -Force
}
Copy-Item -Path $scriptPath -Destination $scriptsDir -Force Copy-Item -Path $scriptPath -Destination $scriptsDir -Force
# Debug: Verify copy
$newSize = (Get-Item $destScript).Length
Write-Host " [DEBUG] Copied $script (new size: $newSize bytes)" -ForegroundColor Magenta
$copiedCount++ $copiedCount++
} else { } else {
Write-Warning "Script not found: $script" Write-Warning "Script not found: $script"

View File

@@ -0,0 +1,542 @@
<#
.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"
}
}
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

View File

@@ -0,0 +1,847 @@
<#
.SYNOPSIS
ROA2WEB - Interactive Build & Publish Console
.DESCRIPTION
Build deployment packages locally and transfer to Windows Server:
- Build locally (Frontend, Backend, Telegram Bot, or All)
- Transfer via Windows Share (LAN) or SSH/SCP (remote)
- Auto-detect best transfer method
- Interactive menu or command-line operation
- Server auto-deploys when new package detected
.PARAMETER NonInteractive
Run in non-interactive mode with specific action
.PARAMETER Action
Action to perform in non-interactive mode:
- Build: Build and publish package
- TestConnections: Test Windows Share and SSH connectivity
- Configure: Edit configuration (interactive only)
- ViewConfig: Display current configuration
.PARAMETER Component
Component to build (All, Frontend, Backend, TelegramBot)
.PARAMETER TransferMethod
Transfer method (Auto, WindowsShare, SSH)
.PARAMETER ConfigFile
Path to configuration file (default: deploy-config.json)
.EXAMPLE
.\Publish-And-Deploy.ps1
Launch interactive menu
.EXAMPLE
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component All
Build all components and publish (auto-detect transfer method)
.EXAMPLE
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component Frontend -TransferMethod SSH
Build frontend and publish via SSH
.NOTES
Author: ROA2WEB Team
Version: 1.0 (Interactive Build & Publish)
Requires: PowerShell 5.1+, SSH key configured for remote access
#>
[CmdletBinding()]
param(
[switch]$NonInteractive,
[ValidateSet("Build", "TestConnections", "ViewConfig")]
[string]$Action = "",
[ValidateSet("All", "Frontend", "Backend", "TelegramBot")]
[string]$Component = "",
[ValidateSet("Auto", "WindowsShare", "SSH")]
[string]$TransferMethod = "",
[string]$ConfigFile = ""
)
$ErrorActionPreference = "Stop"
# =============================================================================
# CONFIGURATION LOADING
# =============================================================================
$script:ConfigFilePath = if ($ConfigFile) {
$ConfigFile
} else {
Join-Path $PSScriptRoot "deploy-config.json"
}
function Load-Configuration {
Write-Host "`nLoading configuration from: $script:ConfigFilePath" -ForegroundColor Gray
if (-not (Test-Path $script:ConfigFilePath)) {
Write-Host "[ERROR] Configuration file not found: $script:ConfigFilePath" -ForegroundColor Red
Write-Host "Creating default configuration..." -ForegroundColor Yellow
$defaultConfig = @{
server = @{
host = "10.0.20.36"
sshPort = 22122
sshUser = "Administrator"
sshKeyPath = "~/.ssh/roa2web_deploy"
windowsShare = "\\10.0.20.36\ROA2WEB-Deploy"
deployPath = "C:\ROA2WEB-Deploy"
}
build = @{
defaultComponent = "All"
outputPath = "../deploy-package"
}
transfer = @{
defaultMethod = "Auto"
scpRemotePath = "/cygdrive/c/ROA2WEB-Deploy"
retryAttempts = 3
timeout = 300
}
deployment = @{
autoDeployEnabled = $true
checkIntervalMinutes = 5
}
}
$defaultConfig | ConvertTo-Json -Depth 10 | Set-Content -Path $script:ConfigFilePath
Write-Host "Default configuration created. Please review and edit as needed." -ForegroundColor Green
}
try {
$script:Config = Get-Content -Path $script:ConfigFilePath -Raw | ConvertFrom-Json
Write-Host "✓ Configuration loaded successfully" -ForegroundColor Green
return $true
} catch {
Write-Host "[ERROR] Failed to load configuration: $_" -ForegroundColor Red
return $false
}
}
function Save-Configuration {
try {
$script:Config | ConvertTo-Json -Depth 10 | Set-Content -Path $script:ConfigFilePath
Write-Host "✓ Configuration saved successfully" -ForegroundColor Green
return $true
} catch {
Write-Host "[ERROR] Failed to save configuration: $_" -ForegroundColor Red
return $false
}
}
# =============================================================================
# HELPER FUNCTIONS
# =============================================================================
function Write-Step {
param([string]$Message)
Write-Host "`n[*] $Message" -ForegroundColor Cyan
}
function Write-Success {
param([string]$Message)
Write-Host " [OK] $Message" -ForegroundColor Green
}
function Write-Error {
param([string]$Message)
Write-Host " [ERROR] $Message" -ForegroundColor Red
}
function Write-Warning {
param([string]$Message)
Write-Host " [WARN] $Message" -ForegroundColor Yellow
}
function Write-Info {
param([string]$Message)
Write-Host " [*] $Message" -ForegroundColor Yellow
}
function Resolve-FullPath {
param([string]$Path)
# Resolve relative paths from the parent of the scripts directory
# This matches the resolution logic in Build-ROA2WEB.ps1
$scriptDir = Split-Path -Parent $PSScriptRoot
$fullPath = Join-Path $scriptDir $Path
$fullPath = [System.IO.Path]::GetFullPath($fullPath)
return $fullPath
}
function Wait-ForKeyPress {
Write-Host "`nPress any key to continue..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
# =============================================================================
# CONNECTION TESTING
# =============================================================================
function Test-WindowsShare {
Write-Step "Testing Windows Share access..."
$sharePath = $script:Config.server.windowsShare
Write-Info "Share: $sharePath"
try {
if (Test-Path $sharePath) {
Write-Success "Windows Share is accessible"
# Try to write test file
$testFile = Join-Path $sharePath ".test-$(Get-Date -Format 'yyyyMMddHHmmss')"
"test" | Out-File -FilePath $testFile -Force
if (Test-Path $testFile) {
Remove-Item -Path $testFile -Force
Write-Success "Write permissions verified"
return $true
} else {
Write-Warning "Share is readable but write test failed"
return $false
}
} else {
Write-Error "Windows Share is not accessible"
Write-Info "Ensure network share is configured and accessible"
return $false
}
} catch {
Write-Error "Failed to access Windows Share: $_"
return $false
}
}
function Test-SSHConnection {
Write-Step "Testing SSH connection..."
$serverHost = $script:Config.server.host
$port = $script:Config.server.sshPort
$user = $script:Config.server.sshUser
$keyPath = $script:Config.server.sshKeyPath
Write-Info "Host: ${serverHost}:${port}"
Write-Info "User: $user"
Write-Info "Key: $keyPath"
# Expand tilde in key path (support both Unix / and Windows \ styles)
if ($keyPath -like "~/*" -or $keyPath -like "~\*") {
$keyPath = $keyPath -replace "^~", $env:USERPROFILE
}
if (-not (Test-Path $keyPath)) {
Write-Error "SSH key not found: $keyPath"
return $false
}
try {
# Test SSH connection
$sshTest = ssh -i "$keyPath" -p $port -o ConnectTimeout=10 -o StrictHostKeyChecking=no "${user}@${serverHost}" "echo 'Connection successful'" 2>&1
if ($LASTEXITCODE -eq 0 -and $sshTest -like "*Connection successful*") {
Write-Success "SSH connection established"
return $true
} else {
Write-Error "SSH connection failed"
Write-Info "Output: $sshTest"
return $false
}
} catch {
Write-Error "Failed to test SSH connection: $_"
return $false
}
}
function Test-AllConnections {
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Connection Testing" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
$shareOk = Test-WindowsShare
$sshOk = Test-SSHConnection
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Test Results" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host "`n Windows Share: " -NoNewline
if ($shareOk) {
Write-Host "✓ Available" -ForegroundColor Green
} else {
Write-Host "✗ Not Available" -ForegroundColor Red
}
Write-Host " SSH Connection: " -NoNewline
if ($sshOk) {
Write-Host "✓ Available" -ForegroundColor Green
} else {
Write-Host "✗ Not Available" -ForegroundColor Red
}
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
return @{
WindowsShare = $shareOk
SSH = $sshOk
}
}
# =============================================================================
# BUILD & TRANSFER FUNCTIONS
# =============================================================================
function Invoke-Build {
param(
[Parameter(Mandatory)]
[ValidateSet("All", "Frontend", "Backend", "TelegramBot")]
[string]$Component
)
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Building Component: $Component" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
$buildScript = Join-Path $PSScriptRoot "Build-ROA2WEB.ps1"
if (-not (Test-Path $buildScript)) {
Write-Error "Build script not found: $buildScript"
return $false
}
try {
$outputPath = $script:Config.build.outputPath
# Resolve the output path using the same logic as Build-ROA2WEB.ps1
$resolvedOutputPath = if ([System.IO.Path]::IsPathRooted($outputPath)) {
$outputPath
} else {
Resolve-FullPath -Path $outputPath
}
Write-Step "Building deployment package..."
& $buildScript -Component $Component -OutputPath $outputPath
if ($LASTEXITCODE -ne 0) {
throw "Build script returned error code: $LASTEXITCODE"
}
if (-not (Test-Path $resolvedOutputPath)) {
throw "Build output not found: $resolvedOutputPath"
}
Write-Success "Build completed successfully"
return $true
} catch {
Write-Error "Build failed: $_"
return $false
}
}
function Copy-ViaWindowsShare {
param([string]$SourcePath)
Write-Step "Transferring via Windows Share..."
$sharePath = $script:Config.server.windowsShare
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$deployFolder = "deploy-$timestamp"
$destPath = Join-Path $sharePath $deployFolder
try {
Write-Info "Destination: $destPath"
Copy-Item -Path $SourcePath -Destination $destPath -Recurse -Force
if (Test-Path $destPath) {
Write-Success "Files transferred successfully"
Write-Success "Server will auto-deploy within $($script:Config.deployment.checkIntervalMinutes) minutes"
return $true
} else {
Write-Error "Transfer failed - destination folder not found"
return $false
}
} catch {
Write-Error "Windows Share transfer failed: $_"
return $false
}
}
function Copy-ViaSSH {
param([string]$SourcePath)
Write-Step "Transferring via SSH/SCP..."
$serverHost = $script:Config.server.host
$port = $script:Config.server.sshPort
$user = $script:Config.server.sshUser
$keyPath = $script:Config.server.sshKeyPath
$remotePath = $script:Config.transfer.scpRemotePath
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$deployFolder = "deploy-$timestamp"
# Expand tilde in key path (support both Unix / and Windows \ styles)
if ($keyPath -like "~/*" -or $keyPath -like "~\*") {
$keyPath = $keyPath -replace "^~", $env:USERPROFILE
}
try {
Write-Info "Host: ${serverHost}:${port}"
Write-Info "Destination: ${remotePath}/${deployFolder}"
# Create remote directory
Write-Info "Creating remote directory..."
$createDir = ssh -i "$keyPath" -p $port "${user}@${serverHost}" "mkdir -p '${remotePath}/${deployFolder}'" 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Failed to create remote directory: $createDir"
}
# Transfer files via SCP
Write-Info "Transferring files (this may take a few minutes)..."
$scpResult = scp -i "$keyPath" -P $port -r "${SourcePath}/*" "${user}@${serverHost}:${remotePath}/${deployFolder}/" 2>&1
if ($LASTEXITCODE -ne 0) {
throw "SCP transfer failed: $scpResult"
}
Write-Success "Files transferred successfully"
Write-Success "Server will auto-deploy within $($script:Config.deployment.checkIntervalMinutes) minutes"
return $true
} catch {
Write-Error "SSH transfer failed: $_"
return $false
}
}
function Invoke-Transfer {
param(
[Parameter(Mandatory)]
[ValidateSet("Auto", "WindowsShare", "SSH")]
[string]$Method,
[Parameter(Mandatory)]
[string]$SourcePath
)
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Transfer Method: $Method" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
if ($Method -eq "Auto") {
Write-Info "Auto-detecting best transfer method..."
# Try Windows Share first (faster for LAN)
Write-Info "Checking Windows Share availability..."
if (Test-Path $script:Config.server.windowsShare) {
Write-Success "Windows Share accessible - using Windows Share"
return Copy-ViaWindowsShare -SourcePath $SourcePath
} else {
Write-Warning "Windows Share not accessible - falling back to SSH"
return Copy-ViaSSH -SourcePath $SourcePath
}
} elseif ($Method -eq "WindowsShare") {
return Copy-ViaWindowsShare -SourcePath $SourcePath
} else {
return Copy-ViaSSH -SourcePath $SourcePath
}
}
# =============================================================================
# CONFIGURATION MANAGEMENT
# =============================================================================
function Show-CurrentConfig {
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Current Configuration" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " Server Settings:" -ForegroundColor Yellow
Write-Host " Host: $($script:Config.server.host)" -ForegroundColor Gray
Write-Host " SSH Port: $($script:Config.server.sshPort)" -ForegroundColor Gray
Write-Host " SSH User: $($script:Config.server.sshUser)" -ForegroundColor Gray
Write-Host " SSH Key: $($script:Config.server.sshKeyPath)" -ForegroundColor Gray
Write-Host " Windows Share: $($script:Config.server.windowsShare)" -ForegroundColor Gray
Write-Host " Deploy Path: $($script:Config.server.deployPath)" -ForegroundColor Gray
Write-Host ""
Write-Host " Build Settings:" -ForegroundColor Yellow
Write-Host " Default Component: $($script:Config.build.defaultComponent)" -ForegroundColor Gray
Write-Host " Output Path: $($script:Config.build.outputPath)" -ForegroundColor Gray
Write-Host ""
Write-Host " Transfer Settings:" -ForegroundColor Yellow
Write-Host " Default Method: $($script:Config.transfer.defaultMethod)" -ForegroundColor Gray
Write-Host " SCP Remote Path: $($script:Config.transfer.scpRemotePath)" -ForegroundColor Gray
Write-Host " Retry Attempts: $($script:Config.transfer.retryAttempts)" -ForegroundColor Gray
Write-Host " Timeout (sec): $($script:Config.transfer.timeout)" -ForegroundColor Gray
Write-Host ""
Write-Host " Deployment Settings:" -ForegroundColor Yellow
Write-Host " Auto-Deploy: $($script:Config.deployment.autoDeployEnabled)" -ForegroundColor Gray
Write-Host " Check Interval: $($script:Config.deployment.checkIntervalMinutes) minutes" -ForegroundColor Gray
Write-Host ""
Write-Host " Config File: $script:ConfigFilePath" -ForegroundColor Gray
Write-Host ("=" * 70) -ForegroundColor Cyan
}
function Edit-Configuration {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Configuration Editor" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " Current Settings:" -ForegroundColor Yellow
Write-Host " Server Host: $($script:Config.server.host)" -ForegroundColor Gray
Write-Host " SSH Port: $($script:Config.server.sshPort)" -ForegroundColor Gray
Write-Host " SSH User: $($script:Config.server.sshUser)" -ForegroundColor Gray
Write-Host " SSH Key: $($script:Config.server.sshKeyPath)" -ForegroundColor Gray
Write-Host " Windows Share: $($script:Config.server.windowsShare)" -ForegroundColor Gray
Write-Host " Server Deploy Path: $($script:Config.server.deployPath)" -ForegroundColor Gray
Write-Host ""
Write-Host " [1] Edit SSH Connection Settings" -ForegroundColor White
Write-Host " [2] Edit Windows Share Path" -ForegroundColor White
Write-Host " [3] Edit Server Deploy Path" -ForegroundColor White
Write-Host " [4] Edit Build Settings" -ForegroundColor White
Write-Host " [5] Edit Transfer Settings" -ForegroundColor White
Write-Host " [S] Save Configuration" -ForegroundColor Green
Write-Host " [R] Reset to Defaults" -ForegroundColor Yellow
Write-Host " [B] Back (discard changes)" -ForegroundColor Red
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Cyan
do {
Write-Host "`nYour choice: " -ForegroundColor Yellow -NoNewline
$choice = Read-Host
switch ($choice.ToUpper()) {
"1" {
Write-Host "`nEdit SSH Connection Settings:" -ForegroundColor Yellow
Write-Host "Server Host [current: $($script:Config.server.host)]: " -NoNewline
$newHost = Read-Host
if ($newHost) { $script:Config.server.host = $newHost }
Write-Host "SSH Port [current: $($script:Config.server.sshPort)]: " -NoNewline
$newPort = Read-Host
if ($newPort) { $script:Config.server.sshPort = [int]$newPort }
Write-Host "SSH User [current: $($script:Config.server.sshUser)]: " -NoNewline
$newUser = Read-Host
if ($newUser) { $script:Config.server.sshUser = $newUser }
Write-Host "SSH Key Path [current: $($script:Config.server.sshKeyPath)]: " -NoNewline
$newKey = Read-Host
if ($newKey) { $script:Config.server.sshKeyPath = $newKey }
Write-Success "SSH settings updated (not saved yet)"
return Edit-Configuration
}
"2" {
Write-Host "`nEdit Windows Share Path:" -ForegroundColor Yellow
Write-Host "Windows Share [current: $($script:Config.server.windowsShare)]: " -NoNewline
$newShare = Read-Host
if ($newShare) { $script:Config.server.windowsShare = $newShare }
Write-Success "Windows Share updated (not saved yet)"
return Edit-Configuration
}
"3" {
Write-Host "`nEdit Server Deploy Path:" -ForegroundColor Yellow
Write-Host "Deploy Path [current: $($script:Config.server.deployPath)]: " -NoNewline
$newPath = Read-Host
if ($newPath) { $script:Config.server.deployPath = $newPath }
Write-Success "Deploy path updated (not saved yet)"
return Edit-Configuration
}
"4" {
Write-Host "`nEdit Build Settings:" -ForegroundColor Yellow
Write-Host "Default Component (All/Frontend/Backend/TelegramBot) [current: $($script:Config.build.defaultComponent)]: " -NoNewline
$newComp = Read-Host
if ($newComp) { $script:Config.build.defaultComponent = $newComp }
Write-Host "Output Path [current: $($script:Config.build.outputPath)]: " -NoNewline
$newOut = Read-Host
if ($newOut) { $script:Config.build.outputPath = $newOut }
Write-Success "Build settings updated (not saved yet)"
return Edit-Configuration
}
"5" {
Write-Host "`nEdit Transfer Settings:" -ForegroundColor Yellow
Write-Host "Default Method (Auto/WindowsShare/SSH) [current: $($script:Config.transfer.defaultMethod)]: " -NoNewline
$newMethod = Read-Host
if ($newMethod) { $script:Config.transfer.defaultMethod = $newMethod }
Write-Host "SCP Remote Path [current: $($script:Config.transfer.scpRemotePath)]: " -NoNewline
$newScp = Read-Host
if ($newScp) { $script:Config.transfer.scpRemotePath = $newScp }
Write-Success "Transfer settings updated (not saved yet)"
return Edit-Configuration
}
"S" {
if (Save-Configuration) {
Write-Host "`nConfiguration saved successfully!" -ForegroundColor Green
Wait-ForKeyPress
return
}
}
"R" {
Write-Host "`nAre you sure you want to reset to defaults? [Y/N]: " -ForegroundColor Yellow -NoNewline
$confirm = Read-Host
if ($confirm.ToUpper() -eq "Y") {
Load-Configuration
Write-Success "Configuration reset to defaults"
return Edit-Configuration
}
}
"B" {
Write-Host "`nChanges discarded" -ForegroundColor Yellow
Load-Configuration # Reload from file
return
}
default {
Write-Host "Invalid choice. Please select 1-5, S, R, or B." -ForegroundColor Red
}
}
} while ($true)
}
# =============================================================================
# MENU FUNCTIONS
# =============================================================================
function Show-MainMenu {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " ROA2WEB - Interactive Build & Publish Console" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " Main Menu:" -ForegroundColor Yellow
Write-Host ""
Write-Host " [1] Build & Publish Package" -ForegroundColor White
Write-Host " (Build locally and transfer to server)" -ForegroundColor Gray
Write-Host ""
Write-Host " [2] Test Connections" -ForegroundColor White
Write-Host " (Verify Windows Share and SSH access)" -ForegroundColor Gray
Write-Host ""
Write-Host " [3] Configure Settings" -ForegroundColor White
Write-Host " (Edit deployment configuration)" -ForegroundColor Gray
Write-Host ""
Write-Host " [4] View Current Configuration" -ForegroundColor White
Write-Host " (Display active 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 "BuildPublish" }
"2" { return "TestConnections" }
"3" { return "Configure" }
"4" { return "ViewConfig" }
"Q" { return "Quit" }
default {
Write-Host "Invalid choice. Please select 1-4 or Q." -ForegroundColor Red
}
}
} while ($true)
}
function Show-ComponentMenu {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Select Component to Build" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " [1] All Components" -ForegroundColor White
Write-Host " (Frontend + Backend + Telegram Bot)" -ForegroundColor Gray
Write-Host ""
Write-Host " [2] Frontend + Backend" -ForegroundColor White
Write-Host " (Vue.js build + FastAPI backend)" -ForegroundColor Gray
Write-Host ""
Write-Host " [3] Backend Only" -ForegroundColor White
Write-Host " (FastAPI backend + shared modules)" -ForegroundColor Gray
Write-Host ""
Write-Host " [4] Telegram Bot Only" -ForegroundColor White
Write-Host " (Telegram bot standalone package)" -ForegroundColor Gray
Write-Host ""
Write-Host " [B] Back to Main Menu" -ForegroundColor Yellow
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Cyan
do {
Write-Host "`nYour choice: " -ForegroundColor Yellow -NoNewline
$choice = Read-Host
switch ($choice.ToUpper()) {
"1" { return "All" }
"2" { return "Frontend" }
"3" { return "Backend" }
"4" { return "TelegramBot" }
"B" { return "Back" }
default {
Write-Host "Invalid choice. Please select 1-4 or B." -ForegroundColor Red
}
}
} while ($true)
}
function Show-TransferMenu {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Select Transfer Method" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " [1] Auto-detect (Recommended)" -ForegroundColor White
Write-Host " Try Windows Share first, fallback to SSH" -ForegroundColor Gray
Write-Host ""
Write-Host " [2] Windows Share (LAN)" -ForegroundColor White
Write-Host " Fast transfer via network share ($($script:Config.server.windowsShare))" -ForegroundColor Gray
Write-Host ""
Write-Host " [3] SSH/SCP (Remote)" -ForegroundColor White
Write-Host " Secure transfer via SSH (port $($script:Config.server.sshPort))" -ForegroundColor Gray
Write-Host ""
Write-Host " [B] Back" -ForegroundColor Yellow
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Cyan
do {
Write-Host "`nYour choice: " -ForegroundColor Yellow -NoNewline
$choice = Read-Host
switch ($choice.ToUpper()) {
"1" { return "Auto" }
"2" { return "WindowsShare" }
"3" { return "SSH" }
"B" { return "Back" }
default {
Write-Host "Invalid choice. Please select 1-3 or B." -ForegroundColor Red
}
}
} while ($true)
}
# =============================================================================
# ACTION HANDLERS
# =============================================================================
function Invoke-BuildAndPublish {
param(
[string]$Component = "",
[string]$TransferMethod = ""
)
# Get component if not provided
if (-not $Component) {
$Component = Show-ComponentMenu
if ($Component -eq "Back") { return }
}
# Get transfer method if not provided
if (-not $TransferMethod) {
$TransferMethod = Show-TransferMenu
if ($TransferMethod -eq "Back") { return }
}
# Build
$buildSuccess = Invoke-Build -Component $Component
if (-not $buildSuccess) {
Write-Host "`n[BUILD FAILED] Cannot proceed with transfer" -ForegroundColor Red
if (-not $NonInteractive) { Wait-ForKeyPress }
return
}
# Transfer
$outputPath = $script:Config.build.outputPath
# Resolve the output path for transfer
$resolvedOutputPath = if ([System.IO.Path]::IsPathRooted($outputPath)) {
$outputPath
} else {
Resolve-FullPath -Path $outputPath
}
$transferSuccess = Invoke-Transfer -Method $TransferMethod -SourcePath $resolvedOutputPath
if ($transferSuccess) {
Write-Host "`n" + ("=" * 70) -ForegroundColor Green
Write-Host " BUILD & PUBLISH COMPLETED SUCCESSFULLY" -ForegroundColor Green
Write-Host ("=" * 70) -ForegroundColor Green
Write-Host "`nPackage published to server: $($script:Config.server.host)" -ForegroundColor Gray
Write-Host "Server will auto-deploy within $($script:Config.deployment.checkIntervalMinutes) minutes" -ForegroundColor Gray
Write-Host ("=" * 70) -ForegroundColor Green
} else {
Write-Host "`n[TRANSFER FAILED] Package built but transfer failed" -ForegroundColor Red
}
if (-not $NonInteractive) { Wait-ForKeyPress }
}
# =============================================================================
# MAIN EXECUTION FLOW
# =============================================================================
function Main {
# Load configuration
if (-not (Load-Configuration)) {
Write-Host "`nPress any key to exit..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit 1
}
# Non-interactive mode
if ($NonInteractive -and $Action) {
switch ($Action) {
"Build" {
$comp = if ($Component) { $Component } else { $script:Config.build.defaultComponent }
$method = if ($TransferMethod) { $TransferMethod } else { $script:Config.transfer.defaultMethod }
Invoke-BuildAndPublish -Component $comp -TransferMethod $method
}
"TestConnections" {
Test-AllConnections | Out-Null
}
"ViewConfig" {
Show-CurrentConfig
}
}
return
}
# Interactive mode - main loop
do {
$mainChoice = Show-MainMenu
switch ($mainChoice) {
"BuildPublish" {
Invoke-BuildAndPublish
}
"TestConnections" {
Test-AllConnections | Out-Null
Wait-ForKeyPress
}
"Configure" {
Edit-Configuration
}
"ViewConfig" {
Show-CurrentConfig
Wait-ForKeyPress
}
"Quit" {
Write-Host "`nGoodbye!`n" -ForegroundColor Cyan
return
}
}
} while ($true)
}
# Run main
Main

View File

@@ -53,8 +53,14 @@ $ErrorActionPreference = "Stop"
# GLOBAL CONFIGURATION # GLOBAL CONFIGURATION
# ============================================================================= # =============================================================================
# Auto-detect source path: if running from scripts/ subdirectory, use parent # Auto-detect source path with priority:
$detectedSourcePath = if ((Split-Path $PSScriptRoot -Leaf) -eq "scripts") { # 1. Environment variable ROA2WEB_SOURCE (set by Check-And-Deploy.ps1)
# 2. If running from scripts/ subdirectory, use parent
# 3. Otherwise use script root
$detectedSourcePath = if ($env:ROA2WEB_SOURCE) {
Write-Host "[INFO] Using source path from environment: $env:ROA2WEB_SOURCE" -ForegroundColor Cyan
$env:ROA2WEB_SOURCE
} elseif ((Split-Path $PSScriptRoot -Leaf) -eq "scripts") {
Split-Path $PSScriptRoot -Parent Split-Path $PSScriptRoot -Parent
} else { } else {
$PSScriptRoot $PSScriptRoot
@@ -1649,16 +1655,27 @@ function Execute-ManageAction {
function Execute-DeployAction { function Execute-DeployAction {
param([string]$Component) param([string]$Component)
# Execute deployment and capture only the boolean result
$success = switch ($Component) { $success = switch ($Component) {
"Backend" { Deploy-Backend } "Backend" {
"TelegramBot" { Deploy-TelegramBot } $result = Deploy-Backend
$result # Explicitly output the boolean
}
"TelegramBot" {
$result = Deploy-TelegramBot
$result # Explicitly output the boolean
}
"All" { "All" {
$backendOk = Deploy-Backend $backendOk = Deploy-Backend
$telegramOk = Deploy-TelegramBot $telegramOk = Deploy-TelegramBot
$backendOk -and $telegramOk # Return combined result
($backendOk -and $telegramOk)
} }
} }
# Ensure $success is boolean
$success = [bool]$success
if ($success) { if ($success) {
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " DEPLOYMENT COMPLETED" -ForegroundColor Green Write-Host " DEPLOYMENT COMPLETED" -ForegroundColor Green
@@ -1668,6 +1685,9 @@ function Execute-DeployAction {
Write-Host " DEPLOYMENT FAILED" -ForegroundColor Red Write-Host " DEPLOYMENT FAILED" -ForegroundColor Red
Write-Host ("=" * 70) -ForegroundColor Cyan Write-Host ("=" * 70) -ForegroundColor Cyan
} }
# Explicitly return boolean value
return $success
} }
function Show-AllStatus { function Show-AllStatus {
@@ -1698,16 +1718,37 @@ function Main {
# Non-interactive mode # Non-interactive mode
if ($NonInteractive -and $Action) { if ($NonInteractive -and $Action) {
switch ($Action) { try {
"DeployBackend" { Execute-DeployAction -Component "Backend" } $success = switch ($Action) {
"DeployTelegramBot" { Execute-DeployAction -Component "TelegramBot" } "DeployBackend" { Execute-DeployAction -Component "Backend" }
"DeployAll" { Execute-DeployAction -Component "All" } "DeployTelegramBot" { Execute-DeployAction -Component "TelegramBot" }
"StartAll" { Execute-ManageAction -Action "Start" -Component "All" } "DeployAll" { Execute-DeployAction -Component "All" }
"StopAll" { Execute-ManageAction -Action "Stop" -Component "All" } "StartAll" { Execute-ManageAction -Action "Start" -Component "All"; $true }
"RestartAll" { Execute-ManageAction -Action "Restart" -Component "All" } "StopAll" { Execute-ManageAction -Action "Stop" -Component "All"; $true }
"Status" { Show-AllStatus } "RestartAll" { Execute-ManageAction -Action "Restart" -Component "All"; $true }
"Status" { Show-AllStatus; $true }
default { $false }
}
# Ensure boolean
$success = [bool]$success
# Debug output
Write-Host "`n[DEBUG] Action: $Action, Success: $success, Type: $($success.GetType().Name)" -ForegroundColor Magenta
if ($success) {
Write-Host "[DEBUG] Exiting with code 0 (success)" -ForegroundColor Magenta
exit 0
} else {
Write-Host "[DEBUG] Exiting with code 1 (failure)" -ForegroundColor Magenta
exit 1
}
} catch {
Write-Host "`n[ERROR] Unhandled exception: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "[ERROR] Stack trace: $($_.ScriptStackTrace)" -ForegroundColor Red
Write-Host "[DEBUG] Exiting with code 1 (exception)" -ForegroundColor Magenta
exit 1
} }
return
} }
# Interactive mode - main loop # Interactive mode - main loop

View File

@@ -0,0 +1,472 @@
<#
.SYNOPSIS
ROA2WEB - Auto-Deploy Setup Wizard (Server-Side)
.DESCRIPTION
Interactive wizard to configure automatic deployment monitoring on Windows Server.
Creates scheduled task that runs Check-And-Deploy.ps1 at specified intervals.
.PARAMETER MonitorPath
Path to monitor for deployment packages (default: C:\Temp)
.PARAMETER ScriptsPath
Path where Check-And-Deploy.ps1 is located (default: C:\Temp\ROA2WEB-Scripts)
.PARAMETER CheckIntervalMinutes
How often to check for new packages in minutes (default: 5)
.PARAMETER AutoDeploy
Automatically deploy new packages when found (default: true)
.PARAMETER CreateScheduledTask
Create Windows Scheduled Task for automation (default: true)
.PARAMETER NonInteractive
Run in non-interactive mode with provided parameters
.EXAMPLE
.\Setup-AutoDeploy.ps1
Launch interactive setup wizard
.EXAMPLE
.\Setup-AutoDeploy.ps1 -NonInteractive -MonitorPath "C:\Temp" -CheckIntervalMinutes 5
Non-interactive setup with defaults
.NOTES
Author: ROA2WEB Team
Version: 1.0 (Auto-Deploy Setup)
Requires: Administrator privileges, PowerShell 5.1+
#>
[CmdletBinding()]
param(
[string]$MonitorPath = "C:\Temp",
[string]$ScriptsPath = "C:\Temp\ROA2WEB-Scripts",
[int]$CheckIntervalMinutes = 5,
[bool]$AutoDeploy = $true,
[bool]$CreateScheduledTask = $true,
[switch]$NonInteractive
)
$ErrorActionPreference = "Stop"
# =============================================================================
# HELPER FUNCTIONS
# =============================================================================
function Write-Step {
param([string]$Message)
Write-Host "`n[*] $Message" -ForegroundColor Cyan
}
function Write-Success {
param([string]$Message)
Write-Host " [OK] $Message" -ForegroundColor Green
}
function Write-Error {
param([string]$Message)
Write-Host " [ERROR] $Message" -ForegroundColor Red
}
function Write-Warning {
param([string]$Message)
Write-Host " [WARN] $Message" -ForegroundColor Yellow
}
function Write-Info {
param([string]$Message)
Write-Host " [*] $Message" -ForegroundColor Yellow
}
# =============================================================================
# WIZARD FUNCTIONS
# =============================================================================
function Show-WizardWelcome {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " ROA2WEB - Auto-Deploy Setup Wizard" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " This wizard will configure automatic deployment monitoring." -ForegroundColor Yellow
Write-Host ""
Write-Host " What it does:" -ForegroundColor White
Write-Host " • Creates directory structure for auto-deployment" -ForegroundColor Gray
Write-Host " • Copies Check-And-Deploy.ps1 to scripts folder" -ForegroundColor Gray
Write-Host " • Creates Windows Scheduled Task for monitoring" -ForegroundColor Gray
Write-Host " • Configures auto-deploy settings" -ForegroundColor Gray
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host "Press any key to continue or Ctrl+C to cancel..." -ForegroundColor Yellow
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
function Get-WizardInput {
param(
[string]$Prompt,
[string]$Default,
[switch]$IsNumber,
[switch]$IsYesNo
)
do {
Write-Host "`n$Prompt" -ForegroundColor Yellow
if ($Default) {
Write-Host " Default: $Default" -ForegroundColor Gray
}
Write-Host " > " -NoNewline -ForegroundColor Cyan
$input = Read-Host
# Use default if empty
if ([string]::IsNullOrWhiteSpace($input) -and $Default) {
return $Default
}
# Validate number
if ($IsNumber) {
$num = 0
if ([int]::TryParse($input, [ref]$num) -and $num -gt 0) {
return $num
} else {
Write-Host " Invalid number. Please enter a positive integer." -ForegroundColor Red
continue
}
}
# Validate Yes/No
if ($IsYesNo) {
$normalized = $input.ToUpper()
if ($normalized -eq "Y" -or $normalized -eq "YES") {
return $true
} elseif ($normalized -eq "N" -or $normalized -eq "NO") {
return $false
} else {
Write-Host " Invalid input. Please enter Y or N." -ForegroundColor Red
continue
}
}
return $input
} while ($true)
}
# =============================================================================
# SETUP FUNCTIONS
# =============================================================================
function Test-Prerequisites {
Write-Step "Checking prerequisites..."
# Check Administrator
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
$isAdmin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
throw "This script requires Administrator privileges"
}
Write-Success "Running as Administrator"
# Check PowerShell version
if ($PSVersionTable.PSVersion.Major -lt 5) {
throw "PowerShell 5.1 or higher is required"
}
Write-Success "PowerShell version: $($PSVersionTable.PSVersion)"
# Check if Check-And-Deploy.ps1 exists
$checkDeployScript = Join-Path $PSScriptRoot "Check-And-Deploy.ps1"
if (-not (Test-Path $checkDeployScript)) {
throw "Check-And-Deploy.ps1 not found in: $PSScriptRoot"
}
Write-Success "Check-And-Deploy.ps1 found"
return $true
}
function New-DirectoryStructure {
param(
[string]$MonitorPath,
[string]$ScriptsPath
)
Write-Step "Creating directory structure..."
# Create monitor path
if (-not (Test-Path $MonitorPath)) {
New-Item -ItemType Directory -Path $MonitorPath -Force | Out-Null
Write-Success "Created: $MonitorPath"
} else {
Write-Success "Already exists: $MonitorPath"
}
# Create scripts path
if (-not (Test-Path $ScriptsPath)) {
New-Item -ItemType Directory -Path $ScriptsPath -Force | Out-Null
Write-Success "Created: $ScriptsPath"
} else {
Write-Success "Already exists: $ScriptsPath"
}
# Create logs directory
$logsPath = Join-Path $ScriptsPath "Logs"
if (-not (Test-Path $logsPath)) {
New-Item -ItemType Directory -Path $logsPath -Force | Out-Null
Write-Success "Created: $logsPath"
} else {
Write-Success "Already exists: $logsPath"
}
return $true
}
function Copy-MonitorScript {
param(
[string]$SourcePath,
[string]$DestPath
)
Write-Step "Copying Check-And-Deploy.ps1 to scripts folder..."
$sourceScript = Join-Path $SourcePath "Check-And-Deploy.ps1"
$destScript = Join-Path $DestPath "Check-And-Deploy.ps1"
# Check if source and destination are the same file
$sourceFull = (Resolve-Path $sourceScript -ErrorAction SilentlyContinue).Path
$destFull = (Resolve-Path $destScript -ErrorAction SilentlyContinue).Path
if ($sourceFull -and $destFull -and ($sourceFull -eq $destFull)) {
Write-Success "Script already in target location: $destScript"
return $destScript
}
try {
Copy-Item -Path $sourceScript -Destination $destScript -Force
Write-Success "Script copied to: $destScript"
return $destScript
} catch {
Write-Error "Failed to copy script: $_"
return $null
}
}
function New-ScheduledTaskForAutoDeployNew {
param(
[string]$ScriptPath,
[int]$IntervalMinutes,
[string]$MonitorPath
)
Write-Step "Creating Windows Scheduled Task..."
$taskName = "ROA2WEB-AutoDeploy"
# Remove existing task if present
$existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existingTask) {
Write-Info "Removing existing task..."
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
}
try {
# Create action
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$ScriptPath`" -WatchPath `"$MonitorPath`""
# Create trigger (repeat indefinitely every X minutes)
# Note: When RepetitionDuration is not specified, the task repeats indefinitely
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
-RepetitionInterval (New-TimeSpan -Minutes $IntervalMinutes)
# Create settings
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-RunOnlyIfNetworkAvailable:$false `
-DontStopOnIdleEnd `
-MultipleInstances IgnoreNew
# Register task (run as SYSTEM)
Register-ScheduledTask -TaskName $taskName `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-User "SYSTEM" `
-RunLevel Highest `
-Description "Monitors $MonitorPath for new deployment packages and auto-deploys them" | Out-Null
Write-Success "Scheduled task created: $taskName"
Write-Success "Interval: Every $IntervalMinutes minutes"
Write-Success "User: SYSTEM"
Write-Success "Status: Enabled"
return $true
} catch {
Write-Error "Failed to create scheduled task: $_"
return $false
}
}
function Show-SetupSummary {
param(
[hashtable]$Config
)
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Setup Summary" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " Monitor Path: $($Config.MonitorPath)" -ForegroundColor Gray
Write-Host " Scripts Path: $($Config.ScriptsPath)" -ForegroundColor Gray
Write-Host " Check Interval: $($Config.CheckIntervalMinutes) minutes" -ForegroundColor Gray
Write-Host " Auto-Deploy: $($Config.AutoDeploy)" -ForegroundColor Gray
Write-Host " Scheduled Task: $($Config.CreateScheduledTask)" -ForegroundColor Gray
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host "Proceed with setup? [Y/N]: " -ForegroundColor Yellow -NoNewline
$confirm = Read-Host
return ($confirm.ToUpper() -eq "Y" -or $confirm.ToUpper() -eq "YES")
}
# =============================================================================
# MAIN SETUP FLOW
# =============================================================================
function Invoke-Setup {
param([hashtable]$Config)
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Starting Setup" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
try {
# Test prerequisites
Test-Prerequisites | Out-Null
# Create directory structure
New-DirectoryStructure -MonitorPath $Config.MonitorPath -ScriptsPath $Config.ScriptsPath | Out-Null
# Copy monitor script
$scriptPath = Copy-MonitorScript -SourcePath $PSScriptRoot -DestPath $Config.ScriptsPath
if (-not $scriptPath) {
throw "Failed to copy monitor script"
}
# Create scheduled task if requested
if ($Config.CreateScheduledTask) {
$taskSuccess = New-ScheduledTaskForAutoDeployNew `
-ScriptPath $scriptPath `
-IntervalMinutes $Config.CheckIntervalMinutes `
-MonitorPath $Config.MonitorPath
if (-not $taskSuccess) {
throw "Failed to create scheduled task"
}
}
Write-Host "`n" + ("=" * 70) -ForegroundColor Green
Write-Host " SETUP COMPLETED SUCCESSFULLY" -ForegroundColor Green
Write-Host ("=" * 70) -ForegroundColor Green
Write-Host ""
Write-Host " Next Steps:" -ForegroundColor Yellow
Write-Host " 1. Deploy packages will be placed in: $($Config.MonitorPath)" -ForegroundColor Gray
Write-Host " 2. Server will check for updates every $($Config.CheckIntervalMinutes) minutes" -ForegroundColor Gray
Write-Host " 3. New packages will be deployed automatically" -ForegroundColor Gray
Write-Host ""
Write-Host " Manual Testing:" -ForegroundColor Yellow
Write-Host " - Test: cd $($Config.ScriptsPath) && .\\Check-And-Deploy.ps1 -Interactive" -ForegroundColor Gray
Write-Host " - View Task: Get-ScheduledTask -TaskName 'ROA2WEB-AutoDeploy'" -ForegroundColor Gray
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Green
return $true
} catch {
Write-Host "`n" + ("=" * 70) -ForegroundColor Red
Write-Host " SETUP FAILED" -ForegroundColor Red
Write-Host ("=" * 70) -ForegroundColor Red
Write-Host ""
Write-Error $_
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Red
return $false
}
}
# =============================================================================
# MAIN EXECUTION FLOW
# =============================================================================
function Main {
# Non-interactive mode
if ($NonInteractive) {
$config = @{
MonitorPath = $MonitorPath
ScriptsPath = $ScriptsPath
CheckIntervalMinutes = $CheckIntervalMinutes
AutoDeploy = $AutoDeploy
CreateScheduledTask = $CreateScheduledTask
}
Invoke-Setup -Config $config | Out-Null
return
}
# Interactive mode - Wizard
Show-WizardWelcome
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Configuration Wizard" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
# Step 1: Monitor Path
$monitorPath = Get-WizardInput -Prompt "[1/5] Deploy Folder Path`n Path to monitor for new deployment packages:" -Default $MonitorPath
# Step 2: Scripts Path
$scriptsPath = Get-WizardInput -Prompt "[2/5] Scripts Folder Path`n Path to store monitoring scripts:" -Default $ScriptsPath
# Step 3: Check Interval
$checkInterval = Get-WizardInput -Prompt "[3/5] Check Interval`n How often to check for updates (minutes):" -Default $CheckIntervalMinutes -IsNumber
# Step 4: Auto-Deploy
Write-Host "`n[4/5] Auto-Deploy Enabled" -ForegroundColor Yellow
Write-Host " Deploy automatically when new package found?" -ForegroundColor Yellow
Write-Host " [Y] Yes (recommended) [N] No (manual trigger only)" -ForegroundColor Gray
$autoDeploy = Get-WizardInput -Prompt " Your choice:" -Default "Y" -IsYesNo
# Step 5: Scheduled Task
Write-Host "`n[5/5] Create Scheduled Task" -ForegroundColor Yellow
Write-Host " Create Windows Scheduled Task for automatic monitoring?" -ForegroundColor Yellow
Write-Host " [Y] Yes [N] No" -ForegroundColor Gray
$createTask = Get-WizardInput -Prompt " Your choice:" -Default "Y" -IsYesNo
# Configuration object
$config = @{
MonitorPath = $monitorPath
ScriptsPath = $scriptsPath
CheckIntervalMinutes = [int]$checkInterval
AutoDeploy = $autoDeploy
CreateScheduledTask = $createTask
}
# Show summary and confirm
$confirmed = Show-SetupSummary -Config $config
if ($confirmed) {
Invoke-Setup -Config $config
} else {
Write-Host "`nSetup cancelled by user" -ForegroundColor Yellow
exit 0
}
Write-Host "`nPress any key to exit..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
# Run main
Main

View File

@@ -0,0 +1,24 @@
{
"server": {
"host": "10.0.20.36",
"sshPort": 22122,
"sshUser": "Administrator",
"sshKeyPath": "~\\.ssh\\id_ed25519",
"windowsShare": "\\\\10.0.20.36\\Temp",
"deployPath": "C:\\Temp"
},
"build": {
"defaultComponent": "All",
"outputPath": "../deploy-package"
},
"transfer": {
"defaultMethod": "Auto",
"scpRemotePath": "C:/Temp",
"retryAttempts": 3,
"timeout": 300
},
"deployment": {
"autoDeployEnabled": true,
"checkIntervalMinutes": 5
}
}