Files
desktop/.gitea/workflows/sshupload.ps1
RoyceDa 0ac5ecee8a /
2026-02-18 15:38:44 +02:00

367 lines
13 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PowerShell script to upload files to SFTP server with remote folder cleanup
# Works on clean Windows without additional utilities (uses WinSCP)
# ==========================================
# PARAMETERS (can override config values via command line)
# ==========================================
param(
[Parameter(Mandatory=$false, HelpMessage="SFTP server IP address or hostname")]
[string]$ServerAddress,
[Parameter(Mandatory=$false, HelpMessage="Username for connection")]
[string]$Username,
[Parameter(Mandatory=$false, HelpMessage="Password for connection")]
[string]$PasswordParam,
[Parameter(Mandatory=$false, HelpMessage="Local file path or pattern (e.g., C:\files\* or dist/builds/x64/Rosetta-*.exe)")]
[string]$LocalFilePath,
[Parameter(Mandatory=$false, HelpMessage="Remote folder on server")]
[string]$RemoteFolderPath,
[Parameter(Mandatory=$false, HelpMessage="SSH port")]
[int]$Port,
[Parameter(Mandatory=$false, HelpMessage="Path to WinSCP executable (auto-detect if not provided)")]
[string]$WinSCPPath
)
# ==========================================
# CONFIGURATION - Default fallback values
# ==========================================
# These values are used only if not provided via command-line parameters or environment variables
$CONFIG_ServerAddress = ""
$CONFIG_Username = ""
$CONFIG_Password = ""
$CONFIG_LocalFilePath = ""
$CONFIG_RemoteFolderPath = ""
$CONFIG_Port = 22
$CONFIG_WinSCPPath = ""
# Priority: Command-line Parameters (highest) > Environment Variables > Config Values (lowest)
# If parameter not provided via command line, check environment variable, then use config value
if (-not $ServerAddress) {
$ServerAddress = if ($env:SFTP_SERVER) { $env:SFTP_SERVER } else { $CONFIG_ServerAddress }
}
if (-not $Username) {
$Username = if ($env:SFTP_USERNAME) { $env:SFTP_USERNAME } else { $CONFIG_Username }
}
# Если пароль передан через CLI (-PasswordParam), используем его даже если пустая строка
if (-not $PSBoundParameters.ContainsKey('PasswordParam')) {
$PasswordParam = if ($env:SFTP_PASSWORD) { $env:SFTP_PASSWORD } else { $CONFIG_Password }
}
if (-not $LocalFilePath) {
$LocalFilePath = if ($env:SFTP_LOCAL_PATH) { $env:SFTP_LOCAL_PATH } else { $CONFIG_LocalFilePath }
}
if (-not $RemoteFolderPath) {
$RemoteFolderPath = if ($env:SFTP_REMOTE_PATH) { $env:SFTP_REMOTE_PATH } else { $CONFIG_RemoteFolderPath }
}
if (-not $Port -or $Port -eq 0) {
$Port = if ($env:SFTP_PORT) { [int]$env:SFTP_PORT } else { $CONFIG_Port }
}
if (-not $WinSCPPath) {
$WinSCPPath = if ($env:WINSCP_PATH) { $env:WINSCP_PATH } else { $CONFIG_WinSCPPath }
}
# Validate required parameters
$requiredParams = @(
@{Name = "ServerAddress"; Value = $ServerAddress},
@{Name = "Username"; Value = $Username},
@{Name = "PasswordParam"; Value = $PasswordParam},
@{Name = "LocalFilePath"; Value = $LocalFilePath},
@{Name = "RemoteFolderPath"; Value = $RemoteFolderPath}
)
$missingParams = @()
foreach ($param in $requiredParams) {
if ([string]::IsNullOrWhiteSpace($param.Value)) {
$missingParams += $param.Name
}
}
if ($missingParams.Count -gt 0) {
Write-Host "ERROR: Missing required parameters: $($missingParams -join ', ')" -ForegroundColor Red
Write-Host "Please configure values in the script CONFIG section or pass them as parameters." -ForegroundColor Red
exit 1
}
# Logging function
function Write-Log {
param(
[Parameter(Mandatory=$false)]
[string]$Message = "(empty message)",
[Parameter(Mandatory=$false)]
[ValidateSet("Info", "Warning", "Error", "Success")]
[string]$Level = "Info"
)
# Handle null or empty messages
if ([string]::IsNullOrWhiteSpace($Message)) {
$Message = "(empty message)"
}
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$color = switch ($Level) {
"Error" { "Red" }
"Warning" { "Yellow" }
"Success" { "Green" }
default { "White" }
}
Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color
}
# Function to find WinSCP installation
function Find-WinSCP {
$possiblePaths = @(
"C:\Program Files\WinSCP\WinSCP.com",
"C:\Program Files (x86)\WinSCP\WinSCP.com",
"C:\Program Files\WinSCP\WinSCP.exe",
"C:\Program Files (x86)\WinSCP\WinSCP.exe",
"C:\Program Files\WinSCP\WinSCPPortable.exe",
"C:\Program Files (x86)\WinSCP\WinSCPPortable.exe"
)
foreach ($path in $possiblePaths) {
if (Test-Path $path) {
Write-Log "Found WinSCP at: $path" "Info"
return $path
}
}
Write-Log "WinSCP not found. Please install it from https://winscp.net/" "Error"
return $null
}
# Main upload function using WinSCP
function Upload-ToSFTP {
param(
[Parameter(Mandatory=$true)]
[string]$Server,
[Parameter(Mandatory=$true)]
[string]$User,
[Parameter(Mandatory=$true)]
[string]$Pass,
[Parameter(Mandatory=$true)]
[string[]]$FileList,
[Parameter(Mandatory=$true)]
[string]$RemotePath,
[Parameter(Mandatory=$true)]
[int]$PortNum,
[Parameter(Mandatory=$true)]
[string]$WinSCPExe
)
# If password came URL-encoded (e.g., %23 for #), decode once
if ($Pass -match '%[0-9A-Fa-f]{2}') {
$Pass = [System.Net.WebUtility]::UrlDecode($Pass)
}
# Escape special characters in password that could break URL or WinSCP syntax
$escapedPassword = $Pass
$escapedPassword = $escapedPassword -replace '@', '%40'
$escapedPassword = $escapedPassword -replace ':', '%3A'
$escapedPassword = $escapedPassword -replace '#', '%23'
$escapedPassword = $escapedPassword -replace '\$', '%24'
$escapedPassword = $escapedPassword -replace '`', '%60'
$escapedPassword = $escapedPassword -replace '&', '%26'
# Create temporary file paths BEFORE script content (needed for variable expansion)
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss_fff'
$debugDir = Join-Path $env:TEMP "winscp_debug"
if (-not (Test-Path $debugDir)) {
New-Item -ItemType Directory -Path $debugDir -Force | Out-Null
}
$scriptPath = Join-Path $debugDir "script_$timestamp.txt"
$logFile = Join-Path $debugDir "log_$timestamp.txt"
$outputPath = Join-Path $debugDir "output_$timestamp.txt"
$errorPath = Join-Path $debugDir "error_$timestamp.txt"
# Create WinSCP script file WITH password (use @"..."@ to expand variables)
$scriptContent = @"
option batch abort
option confirm off
option echo off
option reconnecttime 3
"@
# Add connection string with auto-accept of host key
$scriptContent += "`r`nopen sftp://$User`:$escapedPassword@$Server`:$PortNum -hostkey=`"*`"`r`n"
# Try to clear remote folder by removing all .exe files (ignore if none exist)
$scriptContent += "call rm -f $RemotePath/*.exe`r`n"
# Add files to WinSCP script
if ($FileList.Count -eq 0) {
Write-Log "No files found matching pattern" "Warning"
$scriptContent += "exit`r`n"
}
else {
foreach ($filePath in $FileList) {
# For local Windows paths, keep backslashes as-is (don't convert to forward slashes)
# WinSCP needs native Windows paths for local files
$remoteFilename = Split-Path $filePath -Leaf
$scriptContent += "put `"$filePath`" `"$RemotePath/$remoteFilename`"`r`n"
}
$scriptContent += "close`r`nexit`r`n"
}
# Save script to temporary file
try {
Set-Content -Path $scriptPath -Value $scriptContent -Encoding UTF8
Write-Log "Created WinSCP script at: $scriptPath" "Info"
Write-Log "Script content:" "Info"
Get-Content $scriptPath | ForEach-Object { Write-Log "$_" "Info" }
Write-Log "Executing WinSCP: $WinSCPExe" "Info"
try {
# Determine if this is .com (command-line) or .exe (GUI)
$isCom = $WinSCPExe -like "*.com"
if ($isCom) {
# WinSCP.com uses /log= for logging
$process = Start-Process -FilePath $WinSCPExe `
-ArgumentList "/log=$logFile /script=$scriptPath" `
-NoNewWindow `
-PassThru `
-Wait `
-RedirectStandardOutput $outputPath `
-RedirectStandardError $errorPath
}
else {
# WinSCP.exe (GUI) - needs option logfile in script
$scriptContent += "`r`noption logfile=$logFile"
Set-Content -Path $scriptPath -Value $scriptContent -Encoding UTF8
$process = Start-Process -FilePath $WinSCPExe `
-ArgumentList "/console /script=$scriptPath" `
-NoNewWindow `
-PassThru `
-Wait `
-RedirectStandardOutput $outputPath `
-RedirectStandardError $errorPath
}
}
catch {
Write-Log "Error starting process: $_" "Error"
throw
}
Write-Log "WinSCP process finished with exit code: $($process.ExitCode)" "Info"
# Read WinSCP logs
$winscp_log = Get-Content $logFile -ErrorAction SilentlyContinue -Raw
$output = Get-Content $outputPath -ErrorAction SilentlyContinue -Raw
$error_output = Get-Content $errorPath -ErrorAction SilentlyContinue -Raw
if ($winscp_log) {
Write-Log "WinSCP Log:`r`n$winscp_log" "Info"
}
if ($output) {
Write-Log "Output:`r`n$output" "Info"
}
else {
Write-Log "No standard output from WinSCP" "Info"
}
if ($error_output) {
Write-Log "Standard Error:`r`n$error_output" "Error"
}
if ($process.ExitCode -eq 0) {
Write-Log "Upload completed successfully" "Success"
return $true
}
else {
Write-Log "Upload failed with exit code: $($process.ExitCode)" "Error"
return $false
}
}
catch {
Write-Log "Error during upload: $_" "Error"
return $false
}
finally {
# Cleanup temporary files
Start-Sleep -Milliseconds 500
if (Test-Path $scriptPath) {
Remove-Item $scriptPath -Force -ErrorAction SilentlyContinue
}
if (Test-Path $logFile) {
Remove-Item $logFile -Force -ErrorAction SilentlyContinue
}
if (Test-Path $outputPath) {
Remove-Item $outputPath -Force -ErrorAction SilentlyContinue
}
if (Test-Path $errorPath) {
Remove-Item $errorPath -Force -ErrorAction SilentlyContinue
}
}
}
# =================
# MAIN LOGIC
# =================
Write-Log "========== STARTING FILE UPLOAD PROCESS ==========" "Info"
Write-Log "Server: $ServerAddress`:$Port" "Info"
Write-Log "Username: $Username" "Info"
Write-Log "File pattern: $LocalFilePath" "Info"
Write-Log "Remote folder: $RemoteFolderPath" "Info"
Write-Log "=============================================" "Info"
# Find WinSCP if path not provided
if (-not $WinSCPPath) {
$WinSCPPath = Find-WinSCP
if (-not $WinSCPPath) {
exit 1
}
}
# Verify WinSCP exists
if (-not (Test-Path $WinSCPPath)) {
Write-Log "Error: WinSCP not found at: $WinSCPPath" "Error"
exit 1
}
# Get files matching pattern
$files = @(Get-Item -Path $LocalFilePath -ErrorAction SilentlyContinue | Where-Object {-not $_.PSIsContainer})
if ($files.Count -eq 0) {
Write-Log "Error: No files found matching pattern: $LocalFilePath" "Error"
Write-Log "Current directory: $(Get-Location)" "Error"
Write-Log "Checking if path exists: $(Test-Path $LocalFilePath)" "Error"
exit 1
}
Write-Log "Found $($files.Count) file(s) to upload" "Info"
$filePathList = @($files | ForEach-Object {$_.FullName})
# Perform upload
$success = Upload-ToSFTP -Server $ServerAddress `
-User $Username `
-Pass $PasswordParam `
-FileList $filePathList `
-RemotePath $RemoteFolderPath `
-PortNum $Port `
-WinSCPExe $WinSCPPath
Write-Log "========== PROCESS COMPLETED ==========" "Info"
if ($success) {
exit 0
}
else {
exit 1
}