scripts/setup-strangeloop.ps1
#Requires -Version 7.0 <# .SYNOPSIS strangeloop CLI Setup Script v1.0.0 .DESCRIPTION A comprehensive setup script with advanced phase targeting, environment detection, and project initialization capabilities. Features granular tool modules, environment-specific setup, and a 10-phase execution model. .PARAMETER loop-name The name of the loop to set up (optional) .PARAMETER project-name The name of the project to create (optional) .PARAMETER project-path The parent directory path where the project folder will be created (optional) .PARAMETER from-phase Start execution from this phase and continue through all subsequent phases Valid values: prerequisites, authentication, discovery, environment, wsl, project, git, pipelines, vscode, completion .PARAMETER only-phase Execute only this specific phase (skips all others) Valid values: prerequisites, authentication, discovery, environment, wsl, project, git, pipelines, vscode, completion .PARAMETER skip-phases Array of phases to skip during execution Valid values: prerequisites, authentication, discovery, environment, wsl, project, git, pipelines, vscode, completion .PARAMETER list-phases Display available phases and their descriptions .PARAMETER what-if Preview what actions would be performed without making any actual changes .PARAMETER Verbose Enable verbose output with detailed explanations and additional information .EXAMPLE .\setup-strangeloop.ps1 Run the complete setup process with all phases .EXAMPLE .\setup-strangeloop.ps1 -loop-name "Test-api" -project-name "MyApp" Run setup with specific loop and project names .EXAMPLE .\setup-strangeloop.ps1 -only-phase pipelines -project-name "MyApp" -project-path "/parent/directory" Run only the pipelines phase with project created in /parent/directory/MyApp .EXAMPLE .\setup-strangeloop.ps1 -from-phase environment Start from environment phase and run all subsequent phases .EXAMPLE .\setup-strangeloop.ps1 -skip-phases authentication,wsl Run all phases except authentication and WSL setup .EXAMPLE .\setup-strangeloop.ps1 -what-if Preview what actions would be performed without making any changes .EXAMPLE .\setup-strangeloop.ps1 -Verbose Run with detailed logging and explanations #> [CmdletBinding()] param( [string]${loop-name}, [string]${project-name}, [string]${project-path}, [ValidateSet("prerequisites", "authentication", "discovery", "environment", "wsl", "project", "git", "pipelines", "vscode", "completion")] [string]${from-phase}, [ValidateSet("prerequisites", "authentication", "discovery", "environment", "wsl", "project", "git", "pipelines", "vscode", "completion")] [string]${only-phase}, [ValidateSet("prerequisites", "authentication", "discovery", "environment", "wsl", "project", "git", "pipelines", "vscode", "completion")] [string[]]${skip-phases}, [switch]${list-phases}, [switch]${what-if}, [switch]$Help ) # Set strict mode and error handling Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # Script metadata $ScriptVersion = "1.0.0" # Import shared utilities by dot-sourcing try { . (Join-Path $PSScriptRoot "modules\shared\write-functions.ps1") . (Join-Path $PSScriptRoot "modules\shared\test-functions.ps1") . (Join-Path $PSScriptRoot "modules\shared\loop-functions.ps1") . (Join-Path $PSScriptRoot "modules\shared\phase-functions.ps1") . (Join-Path $PSScriptRoot "modules\shared\validation-functions.ps1") . (Join-Path $PSScriptRoot "modules\shared\display-functions.ps1") } catch { Write-Error "Failed to import shared modules: $($_.Exception.Message)" exit 1 } # Phase execution functions # Individual phase functions # Local Invoke-Phase function compatible with existing calls function Invoke-Phase { param( [string]$PhaseName, [string]$ScriptPath, [hashtable]$Parameters = @{} ) # Add what-if parameter if it was specified and the child script supports it if (${what-if}) { $Parameters['what-if'] = $true Write-Host "🔍 (what-if) Executing Phase: $PhaseName" -ForegroundColor Yellow } try { $fullScriptPath = Join-Path $PSScriptRoot $ScriptPath if (-not (Test-Path $fullScriptPath)) { throw "Phase script not found: $fullScriptPath" } if (${what-if}) { Write-Host "🔍 (what-if) Would execute: $fullScriptPath" -ForegroundColor Yellow # Check if the script supports what-if parameter $scriptContent = Get-Content $fullScriptPath -Raw if ($scriptContent -match '\$\{what-if\}') { Write-Host "🔍 (what-if) Script supports what-if - passing parameter" -ForegroundColor Yellow } else { Write-Host "⚠️ (what-if) Script does not support what-if - would run normally" -ForegroundColor Yellow # For scripts that don't support what-if, we still try to call them but they'll ignore the parameter } } $result = & $fullScriptPath @Parameters return $result } catch { if (${what-if}) { Write-Host "❌ (what-if) Phase '$PhaseName' would fail: $($_.Exception.Message)" -ForegroundColor Red } else { Write-Error "Phase '$PhaseName' failed: $($_.Exception.Message)" throw } } } function Invoke-Prerequisites { param() Write-Host "📋 Phase 1: Prerequisites" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan $result = Invoke-Phase "Prerequisites" "modules\prerequisites\setup-prerequisites.ps1" Write-Host "" return $result } function Invoke-Authentication { param() Write-Host "🔐 Phase 2: Authentication" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan $result = Invoke-Phase "Authentication" "modules\authentication\setup-authentication.ps1" Write-Host "" return $result } function Invoke-Discovery { param( [string]$LoopName ) Write-Host "🔍 Phase 3: Discovery" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan $discoveryResults = $null # If loop name is provided, try to validate it first if ($LoopName) { Write-Host "📋 Validating provided loop: $LoopName" -ForegroundColor Yellow $discoveryResults = Test-LoopAndGetRequirements -LoopName $LoopName if ($discoveryResults) { # Loop is valid, skip discovery phase Write-Success "✓ Loop validation successful - Discovery phase skipped" } else { # Loop is invalid - fail immediately when loop name was explicitly provided Write-Error "Invalid loop name: '$LoopName'. Use -list-phases to see available loops or run without -loop-name for interactive selection." return @{ Success = $false; Phase = "Discovery"; Error = "Invalid loop name provided: $LoopName" } } } else { # No loop name provided, run full discovery $discoveryResults = Invoke-Phase "Discovery" "modules\discovery\setup-discovery.ps1" @{} if ($discoveryResults -and $discoveryResults.Success) { Write-Success "Discovery completed successfully" } else { throw "Discovery phase failed" } } Write-Host "" return $discoveryResults } function Invoke-Environment { param( [string]$LoopName, [hashtable]$DiscoveryResults ) Write-Host "🏗️ Phase 4: Environment Setup" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan $envParams = @{} if ($LoopName) { $envParams['loop-name'] = $LoopName } # Pass discovery results to environment setup if ($DiscoveryResults -and $DiscoveryResults.EnvironmentRequirements) { $envParams['requirements'] = $DiscoveryResults.EnvironmentRequirements } $result = Invoke-Phase "Environment" "modules\environment\setup-environment.ps1" $envParams Write-Host "" return $result } function Invoke-WSLSetup { param( [hashtable]$DiscoveryResults ) $requiresWSL = $false if ($DiscoveryResults -and $DiscoveryResults.EnvironmentRequirements.RequiresWSL) { $requiresWSL = $true } if ($requiresWSL) { Write-Host "🐧 Phase 4.5: WSL Setup" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan $wslParams = @{} $result = Invoke-Phase "WSL Setup" "modules\wsl\initialize-wsl.ps1" $wslParams Write-Host "" return $result } else { Write-Host "🐧 Phase 4.5: WSL Setup" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan Write-Host "ℹ WSL setup skipped - selected loop runs on Windows" -ForegroundColor Blue Write-Host "" return @{ Success = $true; Skipped = $true; Reason = "Not required for Windows loops" } } } function Invoke-ProjectSetup { param( [string]$LoopName, [string]$ProjectName, [string]$ProjectPath, [hashtable]$DiscoveryResults, [string]$BaseDirectory ) Write-Host "🚀 Phase 5: Project Creation and Setup" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan $projectParams = @{} if ($LoopName) { $projectParams['loop-name'] = $LoopName } elseif ($DiscoveryResults -and $DiscoveryResults.SelectedLoop) { $projectParams['loop-name'] = $DiscoveryResults.SelectedLoop } if ($ProjectName) { $projectParams['project-name'] = $ProjectName } if ($ProjectPath) { $projectParams['project-path'] = $ProjectPath } # Validate path compatibility with loop requirements if ($ProjectPath -and $DiscoveryResults -and $DiscoveryResults.EnvironmentRequirements) { $isWindowsPath = $ProjectPath -match '^[A-Za-z]:\\' $isWSLPath = $ProjectPath.StartsWith('/') -or $ProjectPath.Contains('/home/') $requiresWSL = $DiscoveryResults.EnvironmentRequirements.RequiresWSL if ($requiresWSL -and $isWindowsPath) { Write-Error "❌ Path format mismatch: WSL loop '$LoopName' requires a WSL path (e.g., /home/user/...), but Windows path provided: '$ProjectPath'" Write-Host "Please use a WSL path format like: /home/$env:USERNAME/AdsSnR_Containers/services" -ForegroundColor Yellow exit 1 } elseif (-not $requiresWSL -and $isWSLPath) { Write-Error "❌ Path format mismatch: Windows loop '$LoopName' requires a Windows path (e.g., Q:\src\...), but WSL path provided: '$ProjectPath'" Write-Host "Please use a Windows path format like: Q:\src\AdsSnR_Containers\services" -ForegroundColor Yellow exit 1 } } # Pass platform information for proper project creation if ($DiscoveryResults -and $DiscoveryResults.EnvironmentRequirements) { $projectParams['platform'] = $DiscoveryResults.EnvironmentRequirements.Platform $projectParams['requires-wsl'] = $DiscoveryResults.EnvironmentRequirements.RequiresWSL } # Pass the original bootstrap directory as an absolute path for consistent path suggestions if ($BaseDirectory) { $projectParams['base-directory'] = $BaseDirectory } $result = Invoke-Phase "Project Creation and Setup" "modules\project\initialize-project.ps1" $projectParams Write-Host "" return $result } function Invoke-GitSetup { param( [string]$LoopName, [string]$ProjectName, [string]$ProjectPath, [hashtable]$DiscoveryResults, [hashtable]$ProjectResults ) Write-Host "📚 Phase 5.5: Git Source Control Setup" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan # Check if Git phase should be skipped based on project initialization results if ($ProjectResults -and $ProjectResults.ContainsKey('PhaseSkips') -and $ProjectResults.PhaseSkips -and $ProjectResults.PhaseSkips.Git) { Write-Info "⏭️ Skipping Git Source Control Setup - User chose to skip git integration" Write-Host "" return @{ Success = $true; Skipped = $true; Reason = "User choice - no source control" } } # Running in isolation mode if we have required parameters AND the project phase was not actually run $runningInIsolationMode = ($ProjectPath -and $ProjectName -and $LoopName) -and (-not $ProjectResults -or ($ProjectResults -is [hashtable] -and $ProjectResults.ContainsKey('Skipped') -and $ProjectResults.Skipped -eq $true)) if ((-not $ProjectResults -or ($ProjectResults -is [hashtable] -and -not $ProjectResults.Success)) -and -not $runningInIsolationMode) { Write-Warning "⏭️ Skipping Git Source Control Setup - Project creation failed" Write-Info "Git repository setup requires a successfully created project" Write-Host "" return @{ Success = $false; Skipped = $true; Reason = "Project creation failed" } } $gitParams = @{} # Get project details from the project creation results if ($ProjectResults -and $ProjectResults.Success -and $ProjectResults.ContainsKey('ProjectPath')) { $gitParams['project-path'] = $ProjectResults.ProjectPath $gitParams['project-name'] = $ProjectResults.ProjectName $gitParams['loop-name'] = $ProjectResults.LoopName # Pass GitContext information if available if ($ProjectResults.ContainsKey('GitContext')) { $gitParams['git-context'] = $ProjectResults.GitContext } # Set platform and WSL requirements based on discovery results if ($DiscoveryResults -and $DiscoveryResults.EnvironmentRequirements) { $gitParams['platform'] = $DiscoveryResults.EnvironmentRequirements.Platform $gitParams['requires-wsl'] = $DiscoveryResults.EnvironmentRequirements.RequiresWSL } } elseif ($runningInIsolationMode) { # Isolation mode: use provided parameters directly # Note: project-path is now parent directory, so construct full path if ($ProjectPath -and $ProjectName) { if ($ProjectPath.StartsWith("/") -or $ProjectPath.Contains("/home/")) { # WSL path - use forward slash $fullProjectPath = "$ProjectPath/$ProjectName" } else { # Windows path - use Join-Path $fullProjectPath = Join-Path $ProjectPath $ProjectName } $gitParams['project-path'] = $fullProjectPath $gitParams['project-name'] = $ProjectName $gitParams['loop-name'] = $LoopName # Default platform to Windows since we don't have discovery results $gitParams['platform'] = "Windows" $gitParams['requires-wsl'] = $false } } else { # Isolation mode: use command line parameters directly if ($ProjectPath -and $ProjectName -and $LoopName) { # Note: project-path is now parent directory, so construct full path if ($ProjectPath.StartsWith("/") -or $ProjectPath.Contains("/home/")) { # WSL path - use forward slash $fullProjectPath = "$ProjectPath/$ProjectName" } else { # Windows path - use Join-Path $fullProjectPath = Join-Path $ProjectPath $ProjectName } $gitParams['project-path'] = $fullProjectPath $gitParams['project-name'] = $ProjectName $gitParams['loop-name'] = $LoopName # Determine platform based on loop name for isolation mode $windowsOnlyLoops = @('asp-dotnet-framework-api', 'ads-snr-basic', 'flask-windows') if ($LoopName -in $windowsOnlyLoops) { $gitParams['platform'] = 'Windows' $gitParams['requires-wsl'] = $false } else { $gitParams['platform'] = 'WSL' $gitParams['requires-wsl'] = $true } Write-Info "Running Git phase in isolation mode with provided parameters" } else { Write-Warning "Isolation mode requires -project-path, -project-name, and -loop-name parameters" } } if ($gitParams.ContainsKey('project-path')) { $result = Invoke-Phase "Git Source Control Setup" "modules\git\setup-git-source-control.ps1" $gitParams Write-Host "" return $result } else { Write-Warning "No project path available for Git setup" Write-Host "" return @{ Success = $false; Skipped = $true; Reason = "No project path available" } } } function Invoke-PipelineSetup { param( [hashtable]$ProjectResults, [hashtable]$GitResults, [hashtable]$DiscoveryResults, [string]$ProjectName, [switch]$BypassDependencyChecks ) Write-Host "🚀 Phase 6: Pipeline Setup" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan # Check if Pipeline phase should be skipped based on project initialization results if ($ProjectResults -and $ProjectResults.ContainsKey('PhaseSkips') -and $ProjectResults.PhaseSkips -and $ProjectResults.PhaseSkips.Pipeline) { Write-Info "⏭️ Skipping Pipeline Setup - User chose to skip source control/pipelines" Write-Host "" return @{ Success = $true; Skipped = $true; Reason = "User choice - no source control" } } # Skip dependency checks when running as target phase if (-not $BypassDependencyChecks) { # Check if project creation was successful if (-not $ProjectResults -or ($ProjectResults -is [hashtable] -and -not $ProjectResults.Success)) { Write-Warning "⏭️ Skipping Pipeline Setup - Project creation failed" Write-Info "Pipeline setup requires a successfully created project" Write-Host "" return @{ Success = $false; Skipped = $true; Reason = "Project creation failed" } } # Check if Git remote was configured $hasRemoteRepo = $false if ($GitResults -and $GitResults.Success -and -not ($GitResults.ContainsKey('Skipped') -and $GitResults.Skipped)) { # Git setup was completed successfully, check if remote was configured $hasRemoteRepo = $GitResults.RemoteConfigured -eq $true } if (-not $hasRemoteRepo) { if ($GitResults -and $GitResults.ContainsKey('Skipped') -and $GitResults.Skipped) { Write-Warning "⏭️ Skipping Pipeline Setup - Git setup was skipped" Write-Info "Azure DevOps pipelines require Git source control to be set up with a remote repository." Write-Info "To set up pipelines later:" Write-Info " 1. Run Git setup: .\modules\git\setup-git-source-control.ps1" Write-Info " 2. Configure a remote repository during Git setup" Write-Info " 3. Run the pipeline setup: .\modules\pipelines\setup-pipelines-1es.ps1" } else { Write-Warning "⏭️ Skipping Pipeline Setup - No remote Git repository configured" Write-Info "Azure DevOps pipelines require a remote Git repository to be configured." Write-Info "To set up pipelines later:" Write-Info " 1. Configure a remote repository: git remote add origin <repository-url>" Write-Info " 2. Push your code: git push -u origin <branch-name>" Write-Info " 3. Run the pipeline setup script: .\modules\pipelines\setup-pipelines-1es.ps1" } Write-Host "" return @{ Success = $true; Skipped = $true; Reason = "No remote repository configured" } } } else { Write-Info "Running pipeline setup independently - dependency checks bypassed" } # Ask user if they want to set up pipelines Write-Host "" Write-Host "🚀 Azure DevOps Pipeline Setup" -ForegroundColor Yellow Write-Host "This will create Azure DevOps pipelines for your project in the organized folder structure." -ForegroundColor Gray Write-Host "You will be prompted to choose the base location for your pipelines." -ForegroundColor Gray Write-Host "" $setupPipelines = Read-UserPrompt -Prompt "Would you like to set up Azure DevOps pipelines?" -ValidValues @("y","n") if (Test-YesResponse $setupPipelines) { $pipelineParams = @{} # Get project details from the project creation results if ($ProjectResults -and $ProjectResults.Success -and $ProjectResults.ContainsKey('ProjectPath') -and $ProjectResults.ProjectPath) { $pipelineParams['project-path'] = $ProjectResults.ProjectPath $pipelineParams['project-name'] = $ProjectResults.ProjectName # Set WSL requirements based on discovery results if ($DiscoveryResults -and $DiscoveryResults.EnvironmentRequirements -and $DiscoveryResults.EnvironmentRequirements.RequiresWSL) { $pipelineParams['requires-wsl'] = $true } } elseif ($BypassDependencyChecks -and $ProjectName) { # When bypassing dependency checks, use provided project path or prompt Write-Host "" Write-Host "Pipeline Setup - Independent Mode" -ForegroundColor Yellow Write-Host "Since dependency phases were skipped, using provided project details:" -ForegroundColor Gray Write-Host "" $projectPath = ${project-path} if (-not $projectPath) { $projectPath = Read-UserPrompt -Prompt "Project path (absolute path to your project directory)" if (-not $projectPath) { Write-Error "Project path is required for pipeline setup" return @{ Success = $false; Skipped = $true; Reason = "No project path provided" } } } Write-Info "Using project path: $projectPath" $pipelineParams['project-path'] = $projectPath $pipelineParams['project-name'] = $ProjectName # Auto-detect WSL requirements from path or ask if ($projectPath.StartsWith('/') -or $projectPath.Contains('/home/')) { Write-Info "Auto-detected WSL requirement from path format" $pipelineParams['requires-wsl'] = $true } else { $useWSL = Read-UserPrompt -Prompt "Does this project require WSL?" -ValidValues @("y","n") if (Test-YesResponse $useWSL) { $pipelineParams['requires-wsl'] = $true } } } elseif ($DiscoveryResults -and $DiscoveryResults.SelectedLoop) { # Fallback: reconstruct project details if project creation results not available Write-Warning "Project path not available from project results, using fallback path construction" $appName = if ($ProjectName) { $ProjectName } else { "Test-$($DiscoveryResults.SelectedLoop)-app" } if ($DiscoveryResults.EnvironmentRequirements.RequiresWSL) { try { $wslUser = & wsl -- whoami 2>$null if ($wslUser) { $pipelineParams['project-path'] = "/home/$($wslUser.Trim())/AdsSnR_Containers/services/$appName" } else { $pipelineParams['project-path'] = "/home/\$USER/AdsSnR_Containers/services/$appName" } } catch { $pipelineParams['project-path'] = "/home/\$USER/AdsSnR_Containers/services/$appName" } $pipelineParams['requires-wsl'] = $true } else { $pipelineParams['project-path'] = "Q:\src\AdsSnR_Containers\services\$appName" } $pipelineParams['project-name'] = $appName } # Add verbose flag if specified if ($VerbosePreference -eq 'Continue') { $pipelineParams['VerboseLogging'] = $true } if ($pipelineParams.ContainsKey('project-path')) { $result = Invoke-Phase "Pipeline Setup" "modules\pipelines\setup-pipelines-1es.ps1" $pipelineParams Write-Host "" return $result } else { Write-Warning "No project path available for Pipeline setup" Write-Host "" return @{ Success = $false; Skipped = $true; Reason = "No project path available" } } } else { Write-Info "⏭️ Pipeline setup skipped by user choice" Write-Host "" return @{ Success = $true; Skipped = $true; Reason = "User choice" } } } function Invoke-VSCodeSetup { param( [string]$LoopName, [string]$ProjectPath, [hashtable]$ProjectResults, [hashtable]$DiscoveryResults, [string]$ProjectName ) Write-Host "💻 Phase 8: VS Code Integration" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan # Check if project creation was successful if ((-not $ProjectResults) -or ($ProjectResults -is [hashtable] -and -not $ProjectResults.Success)) { Write-Warning "⏭️ Skipping VS Code Integration - Project creation failed" Write-Info "VS Code integration requires a successfully created project" Write-Host "" return @{ Success = $false; Skipped = $true; Reason = "Project creation failed" } } $vscodeParams = @{} # First priority: Use directly provided parameters (for targeted execution) if ($ProjectPath) { $vscodeParams['project-path'] = $ProjectPath } if ($ProjectName) { $vscodeParams['project-name'] = $ProjectName } if ($LoopName) { $vscodeParams['loop-name'] = $LoopName } # Get project path from the project creation results if not provided directly if ($ProjectResults -and -not $vscodeParams.ContainsKey('project-path')) { # Handle different return types if ($ProjectResults -is [hashtable] -and $ProjectResults.ContainsKey('Success') -and $ProjectResults.Success) { # Project returned object with Success property - use the actual path that was created if ($ProjectResults.ContainsKey('ProjectPath')) { $vscodeParams['project-path'] = $ProjectResults.ProjectPath } if ($ProjectResults.ContainsKey('LoopName') -and -not $vscodeParams.ContainsKey('loop-name')) { $vscodeParams['loop-name'] = $ProjectResults.LoopName } # Set platform and WSL requirements based on discovery results if ($DiscoveryResults -and $DiscoveryResults.EnvironmentRequirements) { $vscodeParams['platform'] = $DiscoveryResults.EnvironmentRequirements.Platform $vscodeParams['requires-wsl'] = $DiscoveryResults.EnvironmentRequirements.RequiresWSL } } elseif ($ProjectResults -and $DiscoveryResults -and $DiscoveryResults.ContainsKey('SelectedLoop') -and $DiscoveryResults.SelectedLoop) { # Project succeeded but returned simple value - reconstruct path $appName = if ($ProjectName) { $ProjectName } else { "Test-$($DiscoveryResults.SelectedLoop)-app" } if ($DiscoveryResults.ContainsKey('EnvironmentRequirements') -and $DiscoveryResults.EnvironmentRequirements.RequiresWSL) { try { $wslUser = & wsl -- whoami 2>$null if ($wslUser) { $vscodeParams['project-path'] = "/home/$($wslUser.Trim())/AdsSnR_Containers/services/$appName" } else { $vscodeParams['project-path'] = "/home/\$USER/AdsSnR_Containers/services/$appName" } } catch { $vscodeParams['project-path'] = "/home/\$USER/AdsSnR_Containers/services/$appName" } $vscodeParams['requires-wsl'] = $true } else { $vscodeParams['project-path'] = "Q:\src\AdsSnR_Containers\services\$appName" } $vscodeParams['loop-name'] = $DiscoveryResults.SelectedLoop # Set platform and WSL requirements based on discovery results if ($DiscoveryResults -and $DiscoveryResults.ContainsKey('EnvironmentRequirements') -and $DiscoveryResults.EnvironmentRequirements) { $vscodeParams['platform'] = $DiscoveryResults.EnvironmentRequirements.Platform $vscodeParams['requires-wsl'] = $DiscoveryResults.EnvironmentRequirements.RequiresWSL } } } elseif ($DiscoveryResults -and $DiscoveryResults.ContainsKey('SelectedLoop') -and $DiscoveryResults.SelectedLoop -and -not $vscodeParams.ContainsKey('project-path')) { # Fallback: reconstruct project path if project creation results not available if ($DiscoveryResults.ContainsKey('EnvironmentRequirements') -and $DiscoveryResults.EnvironmentRequirements.RequiresWSL) { # For WSL projects, get the project location that was created $appName = if ($ProjectName) { $ProjectName } else { "Test-$($DiscoveryResults.SelectedLoop)-app" } try { $wslUser = & wsl -- whoami 2>$null if ($wslUser) { $vscodeParams['project-path'] = "/home/$($wslUser.Trim())/AdsSnR_Containers/services/$appName" } else { $vscodeParams['project-path'] = "/home/\$USER/AdsSnR_Containers/services/$appName" } } catch { $vscodeParams['project-path'] = "/home/\$USER/AdsSnR_Containers/services/$appName" } $vscodeParams['requires-wsl'] = $true } else { # For Windows projects - use the standardized path $appName = if ($ProjectName) { $ProjectName } else { "Test-$($DiscoveryResults.SelectedLoop)-app" } $vscodeParams['project-path'] = "Q:\src\AdsSnR_Containers\services\$appName" } if ($DiscoveryResults.ContainsKey('SelectedLoop') -and -not $vscodeParams.ContainsKey('loop-name')) { $vscodeParams['loop-name'] = $DiscoveryResults.SelectedLoop } if ($DiscoveryResults.ContainsKey('EnvironmentRequirements') -and $DiscoveryResults.EnvironmentRequirements.ContainsKey('Platform')) { $vscodeParams['platform'] = $DiscoveryResults.EnvironmentRequirements.Platform } } # Ensure platform and WSL detection for isolated VSCode execution if (-not $vscodeParams.ContainsKey('platform') -and $LoopName) { # Determine environment requirements based on loop type (same logic as discovery module) $windowsOnlyLoops = @('asp-dotnet-framework-api', 'ads-snr-basic', 'flask-windows') $requiresWindows = $LoopName -in $windowsOnlyLoops $requiresWSL = -not $requiresWindows $vscodeParams['platform'] = if ($requiresWindows) { 'Windows' } else { 'WSL' } $vscodeParams['requires-wsl'] = $requiresWSL } if ($vscodeParams.ContainsKey('project-path')) { $result = Invoke-Phase "VS Code Integration" "modules\vscode\setup-vscode.ps1" $vscodeParams Write-Host "" return $result } else { Write-Warning "No project path available for VS Code setup" Write-Host "" return @{ Success = $false; Skipped = $true; Reason = "No project path available" } } } function Invoke-Completion { param( [hashtable]$PhaseResults, [datetime]$StartTime ) Write-Host "✅ Phase 10: Completion and Summary" -ForegroundColor Cyan Write-Host "──────────────────────────────────────────────────────────" -ForegroundColor DarkCyan $completionParams = @{ 'phase-results' = $PhaseResults 'start-time' = $StartTime } $result = Invoke-Phase "Completion and Summary" "modules\completion\setup-completion.ps1" $completionParams Write-Host "" return $result } # Main execution function # Main execution function function Start-Setup { $startTime = Get-Date $phaseResults = @{} $discoveryResults = $null try { Show-Banner -Version $ScriptVersion # Set execution policy to allow script execution try { $currentPolicy = Get-ExecutionPolicy -Scope CurrentUser if ($currentPolicy -eq "Restricted" -or $currentPolicy -eq "Undefined") { Write-Host "🔐 Setting PowerShell execution policy..." -ForegroundColor Yellow Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force Write-Host "✓ PowerShell execution policy set to RemoteSigned for current user" -ForegroundColor Green } else { Write-Host "✓ PowerShell execution policy is already configured ($currentPolicy)" -ForegroundColor Green } } catch { Write-Warning "Could not set execution policy: $($_.Exception.Message)" Write-Warning "You may need to run: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser" } Write-Host "" # Phase 1: Prerequisites if (Test-ShouldRunPhase -PhaseName "prerequisites" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { $phaseResults['Prerequisites'] = Invoke-Prerequisites } else { Write-Host "⏭️ Skipping Phase 1: Prerequisites (not in target/excluded)" -ForegroundColor Yellow $phaseResults['Prerequisites'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } # Phase 2: Authentication if (Test-ShouldRunPhase -PhaseName "authentication" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { $phaseResults['Authentication'] = Invoke-Authentication } else { Write-Host "⏭️ Skipping Phase 2: Authentication (not in target/excluded)" -ForegroundColor Yellow $phaseResults['Authentication'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } # Phase 3: Discovery if (Test-ShouldRunPhase -PhaseName "discovery" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { $discoveryResults = Invoke-Discovery -LoopName ${loop-name} $phaseResults['Discovery'] = $discoveryResults } else { Write-Host "⏭️ Skipping Phase 3: Discovery (not in target/excluded)" -ForegroundColor Yellow $phaseResults['Discovery'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } # Phase 4: Environment Setup if (Test-ShouldRunPhase -PhaseName "environment" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { $phaseResults['Environment'] = Invoke-Environment -LoopName ${loop-name} -DiscoveryResults $discoveryResults } else { Write-Host "⏭️ Skipping Phase 4: Environment Setup (not in target/excluded)" -ForegroundColor Yellow $phaseResults['Environment'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } # Phase 4.5: WSL Setup (only if WSL is required by the selected loop) if (Test-ShouldRunPhase -PhaseName "wsl" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { $phaseResults['WSL'] = Invoke-WSLSetup -DiscoveryResults $discoveryResults } else { Write-Host "⏭️ Skipping Phase 4.5: WSL Setup (not in target/excluded)" -ForegroundColor Yellow $phaseResults['WSL'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } # Phase 5: Project Creation and Setup if (Test-ShouldRunPhase -PhaseName "project" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { $projectResults = Invoke-ProjectSetup -LoopName ${loop-name} -ProjectName ${project-name} -ProjectPath ${project-path} -DiscoveryResults $discoveryResults -BaseDirectory $PSScriptRoot $phaseResults['Project'] = $projectResults } else { Write-Host "⏭️ Skipping Phase 5: Project Creation and Setup (not in target/excluded)" -ForegroundColor Yellow $phaseResults['Project'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } # Phase 5.5: Git Source Control Setup if (Test-ShouldRunPhase -PhaseName "git" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { $phaseResults['Git'] = Invoke-GitSetup -ProjectPath ${project-path} -ProjectName ${project-name} -LoopName ${loop-name} -DiscoveryResults $discoveryResults -ProjectResults $phaseResults['Project'] } else { Write-Host "⏭️ Skipping Phase 5.5: Git Source Control Setup (not in target/excluded)" -ForegroundColor Yellow $phaseResults['Git'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } # Phase 6: Pipeline Setup if (Test-ShouldRunPhase -PhaseName "pipelines" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { # When running pipelines as only phase, bypass dependency checks $isIndependentExecution = ${only-phase} -eq "pipelines" $phaseResults['Pipelines'] = Invoke-PipelineSetup -ProjectResults $phaseResults['Project'] -GitResults $phaseResults['Git'] -DiscoveryResults $discoveryResults -ProjectName ${project-name} -BypassDependencyChecks:$isIndependentExecution } else { Write-Host "⏭️ Skipping Phase 6: Pipeline Setup (not in target/excluded)" -ForegroundColor Yellow $phaseResults['Pipelines'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } # Phase 7: VS Code Integration if (Test-ShouldRunPhase -PhaseName "vscode" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { $phaseResults['VSCode'] = Invoke-VSCodeSetup -LoopName ${loop-name} -ProjectPath ${project-path} -ProjectResults $phaseResults['Project'] -DiscoveryResults $discoveryResults -ProjectName ${project-name} } else { Write-Host "⏭️ Skipping Phase 7: VS Code Integration (not in target/excluded)" -ForegroundColor Yellow $phaseResults['VSCode'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } # Phase 10: Completion and Summary if (Test-ShouldRunPhase -PhaseName "completion" -OnlyPhase ${only-phase} -FromPhase ${from-phase} -SkipPhases ${skip-phases}) { $phaseResults['Completion'] = Invoke-Completion -PhaseResults $phaseResults -StartTime $startTime } else { Write-Host "⏭️ Skipping Phase 10: Completion and Summary (not in target/excluded)" -ForegroundColor Yellow $phaseResults['Completion'] = @{ Success = $true; Skipped = $true; Reason = "Phase filtering" } } } catch { # Handle failure with completion phase try { $completionParams = @{ 'phase-results' = $phaseResults 'start-time' = $startTime 'failed' = $true } & (Join-Path $PSScriptRoot "modules\completion\setup-completion.ps1") @completionParams } catch { # If completion fails too, just show basic error } Write-Host "" Write-Host "╔═══════════════════════════════════════════════════════════════════╗" -ForegroundColor Red Write-Host "║ ❌ SETUP FAILED ║" -ForegroundColor Red Write-Host "║ ║" -ForegroundColor Red Write-Host "║ Error: $($_.Exception.Message.PadRight(58)) ║" -ForegroundColor Red Write-Host "╚═══════════════════════════════════════════════════════════════════╝" -ForegroundColor Red Write-Host "" exit 1 } } # Phase listing function # Parameter validation and help handling # Parameter validation and help handling if (${list-phases}) { Show-Phases -ShowOrder -ShowDependencies exit 0 } if ($Help) { Show-Help -ShowExamples -ShowPhases exit 0 } # Validate parameters using shared validation functions $boundParams = if ($PSBoundParameters) { $PSBoundParameters } else { @{} } $validationResult = Test-ParameterValidation -BoundParameters $boundParams Show-ValidationErrors -ValidationResult $validationResult -ExitOnError # Show what-if notification if in preview mode if (${what-if}) { Write-Host "" Write-Host "🔍 " -NoNewline -ForegroundColor Yellow Write-Host "what-if Mode: Previewing actions without making changes" -ForegroundColor Yellow Write-Host " No actual installations, configurations, or file modifications will be performed" -ForegroundColor Gray Write-Host "" } # Start the setup process Start-Setup |