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:
847
deployment/windows/scripts/Publish-And-Deploy.ps1
Normal file
847
deployment/windows/scripts/Publish-And-Deploy.ps1
Normal 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
|
||||
Reference in New Issue
Block a user