<# .SYNOPSIS Local CI Runner for Windows PowerShell wrapper for local-ci.sh .DESCRIPTION Unified local CI/CD testing runner for StellaOps on Windows. This script wraps the Bash implementation via WSL2 or Git Bash. .PARAMETER Mode The testing mode to run: - smoke : Quick smoke test (unit tests only, ~2 min) - pr : Full PR-gating suite (all required checks, ~15 min) - module : Module-specific tests (auto-detect or specified) - workflow : Simulate specific workflow via act - release : Release simulation (dry-run) - full : All tests including extended categories (~45 min) .PARAMETER Category Specific test category to run (Unit, Architecture, Contract, Integration, Security, Golden) .PARAMETER Module Specific module to test (Scanner, Concelier, Authority, etc.) .PARAMETER Workflow Specific workflow to simulate (for workflow mode) .PARAMETER Docker Force Docker execution mode .PARAMETER Native Force native execution mode .PARAMETER Act Force act execution mode .PARAMETER Parallel Number of parallel test runners (default: auto-detect) .PARAMETER Verbose Enable verbose output .PARAMETER DryRun Show what would run without executing .PARAMETER Rebuild Force rebuild of CI Docker image .PARAMETER NoServices Skip starting CI services .PARAMETER KeepServices Don't stop services after tests .EXAMPLE .\local-ci.ps1 smoke Quick validation before push .EXAMPLE .\local-ci.ps1 pr Full PR check .EXAMPLE .\local-ci.ps1 module -Module Scanner Test specific module .EXAMPLE .\local-ci.ps1 workflow -Workflow test-matrix Simulate specific workflow .NOTES Requires WSL2 or Git Bash to execute the underlying Bash script. For full feature support, use WSL2 with Ubuntu. #> [CmdletBinding()] param( [Parameter(Position = 0)] [ValidateSet('smoke', 'pr', 'module', 'workflow', 'release', 'full')] [string]$Mode = 'smoke', [string]$Category, [string]$Module, [string]$Workflow, [switch]$Docker, [switch]$Native, [switch]$Act, [int]$Parallel, [switch]$Verbose, [switch]$DryRun, [switch]$Rebuild, [switch]$NoServices, [switch]$KeepServices, [switch]$Help ) # Script location $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $RepoRoot = Split-Path -Parent (Split-Path -Parent $ScriptDir) # Show help if requested if ($Help) { Get-Help $MyInvocation.MyCommand.Path -Detailed exit 0 } function Write-ColoredOutput { param( [string]$Message, [ConsoleColor]$Color = [ConsoleColor]::White ) $originalColor = $Host.UI.RawUI.ForegroundColor $Host.UI.RawUI.ForegroundColor = $Color Write-Host $Message $Host.UI.RawUI.ForegroundColor = $originalColor } function Write-Info { Write-ColoredOutput "[INFO] $args" -Color Cyan } function Write-Success { Write-ColoredOutput "[OK] $args" -Color Green } function Write-Warning { Write-ColoredOutput "[WARN] $args" -Color Yellow } function Write-Error { Write-ColoredOutput "[ERROR] $args" -Color Red } # Find Bash executable function Find-BashExecutable { # Priority: WSL2 > Git Bash > Windows Subsystem for Linux (legacy) # Check for WSL $wsl = Get-Command wsl -ErrorAction SilentlyContinue if ($wsl) { # Verify WSL is working $wslCheck = & wsl --status 2>&1 if ($LASTEXITCODE -eq 0) { Write-Info "Using WSL2 for Bash execution" return @{ Type = 'wsl'; Path = 'wsl' } } } # Check for Git Bash $gitBashPaths = @( "C:\Program Files\Git\bin\bash.exe", "C:\Program Files (x86)\Git\bin\bash.exe", "$env:LOCALAPPDATA\Programs\Git\bin\bash.exe" ) foreach ($path in $gitBashPaths) { if (Test-Path $path) { Write-Info "Using Git Bash for execution" return @{ Type = 'gitbash'; Path = $path } } } # Check PATH for bash $bashInPath = Get-Command bash -ErrorAction SilentlyContinue if ($bashInPath) { Write-Info "Using Bash from PATH" return @{ Type = 'path'; Path = $bashInPath.Source } } return $null } # Convert Windows path to Unix path for WSL function Convert-ToUnixPath { param([string]$WindowsPath) if ($WindowsPath -match '^([A-Za-z]):(.*)$') { $drive = $Matches[1].ToLower() $rest = $Matches[2] -replace '\\', '/' return "/mnt/$drive$rest" } return $WindowsPath -replace '\\', '/' } # Build argument list function Build-Arguments { $args = @($Mode) if ($Category) { $args += "--category"; $args += $Category } if ($Module) { $args += "--module"; $args += $Module } if ($Workflow) { $args += "--workflow"; $args += $Workflow } if ($Docker) { $args += "--docker" } if ($Native) { $args += "--native" } if ($Act) { $args += "--act" } if ($Parallel) { $args += "--parallel"; $args += $Parallel } if ($Verbose) { $args += "--verbose" } if ($DryRun) { $args += "--dry-run" } if ($Rebuild) { $args += "--rebuild" } if ($NoServices) { $args += "--no-services" } if ($KeepServices) { $args += "--keep-services" } return $args } # Main execution Write-Host "" Write-Host "=========================================" -ForegroundColor Magenta Write-Host " StellaOps Local CI Runner (Windows) " -ForegroundColor Magenta Write-Host "=========================================" -ForegroundColor Magenta Write-Host "" # Find Bash $bash = Find-BashExecutable if (-not $bash) { Write-Error "Bash not found. Please install one of the following:" Write-Host " - WSL2: https://docs.microsoft.com/en-us/windows/wsl/install" Write-Host " - Git for Windows: https://git-scm.com/download/win" exit 1 } # Build script path $scriptPath = Join-Path $ScriptDir "local-ci.sh" if (-not (Test-Path $scriptPath)) { Write-Error "Script not found: $scriptPath" exit 1 } # Build arguments $bashArgs = Build-Arguments Write-Info "Mode: $Mode" Write-Info "Bash: $($bash.Type)" Write-Info "Repository: $RepoRoot" Write-Host "" # Execute based on Bash type try { switch ($bash.Type) { 'wsl' { $unixScript = Convert-ToUnixPath $scriptPath Write-Info "Executing: wsl bash $unixScript $($bashArgs -join ' ')" & wsl bash $unixScript @bashArgs } 'gitbash' { # Git Bash uses its own path conversion $unixScript = $scriptPath -replace '\\', '/' Write-Info "Executing: $($bash.Path) $unixScript $($bashArgs -join ' ')" & $bash.Path $unixScript @bashArgs } 'path' { Write-Info "Executing: bash $scriptPath $($bashArgs -join ' ')" & bash $scriptPath @bashArgs } } $exitCode = $LASTEXITCODE } catch { Write-Error "Execution failed: $_" $exitCode = 1 } # Report result Write-Host "" if ($exitCode -eq 0) { Write-Success "Local CI completed successfully!" } else { Write-Error "Local CI failed with exit code: $exitCode" } exit $exitCode