build.ps1
#requires -Version 5.1 <# .SYNOPSIS Build script for the power.rapt PowerShell module. .DESCRIPTION This script provides various build tasks for the power.rapt module including: - Clean: Remove build artifacts - Analyze: Run PSScriptAnalyzer - Test: Run Pester tests - Build: Create module package - Publish: Publish to PowerShell Gallery .PARAMETER Task The build task to execute. Valid values: Clean, Analyze, Test, Build, Publish .PARAMETER Configuration The build configuration. Valid values: Debug, Release (default) .PARAMETER ModuleName The name of the module. Defaults to 'power.rapt' .PARAMETER OutputPath The output path for build artifacts. Defaults to '.\Output' .EXAMPLE .\build.ps1 -Task Build .EXAMPLE .\build.ps1 -Task Test -Configuration Debug .NOTES Requires Pester and PSScriptAnalyzer modules for testing and analysis. #> [CmdletBinding()] param( [Parameter()] [ValidateSet('Clean', 'Analyze', 'Test', 'Build', 'Publish', 'Default')] [string]$Task = 'Default', [Parameter()] [ValidateSet('Debug', 'Release')] [string]$Configuration = 'Release', [Parameter()] [string]$ModuleName = 'power.rapt', [Parameter()] [string]$OutputPath = (Join-Path $PSScriptRoot 'Output') ) # Set build variables $BuildVariables = @{ ModuleName = $ModuleName Configuration = $Configuration OutputPath = $OutputPath SourcePath = $PSScriptRoot ModulePath = Join-Path $PSScriptRoot "$ModuleName.psm1" ManifestPath = Join-Path $PSScriptRoot "$ModuleName.psd1" TestPath = Join-Path $PSScriptRoot 'tests' DocsPath = Join-Path $PSScriptRoot 'docs' PublicPath = Join-Path $PSScriptRoot 'Public' PrivatePath = Join-Path $PSScriptRoot 'Private' AnalyzerPath = Join-Path $PSScriptRoot 'ScriptAnalyzerSettings.psd1' } # Helper functions function Write-BuildMessage { param( [string]$Message, [string]$Level = 'Info' ) $timestamp = Get-Date -Format 'HH:mm:ss' switch ($Level) { 'Success' { Write-Information "[$timestamp] ✅ $Message" -InformationAction Continue } 'Warning' { Write-Warning "[$timestamp] ⚠️ $Message" } 'Error' { Write-Error "[$timestamp] ❌ $Message" } default { Write-Information "[$timestamp] ℹ️ $Message" -InformationAction Continue } } } function Install-RequiredModule { Write-BuildMessage "Checking required modules..." $requiredModules = @('Pester', 'PSScriptAnalyzer', 'PowerShellGet') $missingModules = @() foreach ($module in $requiredModules) { if (-not (Get-Module -Name $module -ListAvailable)) { $missingModules += $module } } if ($missingModules.Count -gt 0) { Write-BuildMessage "Installing missing modules: $($missingModules -join ', ')" -Level Warning foreach ($module in $missingModules) { try { Install-Module -Name $module -Scope CurrentUser -Force -AllowClobber Write-BuildMessage "Installed $module" -Level Success } catch { Write-BuildMessage "Failed to install $module`: $($_.Exception.Message)" -Level Error throw } } } else { Write-BuildMessage "All required modules are available" -Level Success } } function Invoke-Clean { Write-BuildMessage " === = CLEAN TASK === = " -Level Info Write-BuildMessage "Cleaning output directory: $($BuildVariables.OutputPath)" if (Test-Path $BuildVariables.OutputPath) { Remove-Item $BuildVariables.OutputPath -Recurse -Force Write-BuildMessage "Removed existing output directory" -Level Success } New-Item -Path $BuildVariables.OutputPath -ItemType Directory -Force | Out-Null Write-BuildMessage "Created clean output directory" -Level Success } function Invoke-Analyze { Write-BuildMessage " === = ANALYZE TASK === = " -Level Info Write-BuildMessage "Running PSScriptAnalyzer..." $analyzerResults = @() $filesToAnalyze = @() # Add core module files if (Test-Path $BuildVariables.ModulePath) { $filesToAnalyze += $BuildVariables.ModulePath } if (Test-Path $BuildVariables.ManifestPath) { $filesToAnalyze += $BuildVariables.ManifestPath } # Add function files if (Test-Path $BuildVariables.PublicPath) { $filesToAnalyze += Get-ChildItem -Path $BuildVariables.PublicPath -Filter '*.ps1' -Recurse } if (Test-Path $BuildVariables.PrivatePath) { $filesToAnalyze += Get-ChildItem -Path $BuildVariables.PrivatePath -Filter '*.ps1' -Recurse } Write-BuildMessage "Analyzing $($filesToAnalyze.Count) files..." foreach ($file in $filesToAnalyze) { if (Test-Path $file) { Write-BuildMessage " Analyzing: $($file.Name)" -Level Info $results = if (Test-Path $BuildVariables.AnalyzerPath) { Invoke-ScriptAnalyzer -Path $file -Settings $BuildVariables.AnalyzerPath } else { Invoke-ScriptAnalyzer -Path $file } $analyzerResults += $results } } if ($analyzerResults) { $errorCount = ($analyzerResults | Where-Object Severity -EQ 'Error').Count $warningCount = ($analyzerResults | Where-Object Severity -EQ 'Warning').Count $infoCount = ($analyzerResults | Where-Object Severity -EQ 'Information').Count Write-BuildMessage "PSScriptAnalyzer Results:" -Level Info Write-BuildMessage " Errors: $errorCount" -Level $(if ($errorCount -gt 0) { 'Error' } else { 'Info' }) Write-BuildMessage " Warnings: $warningCount" -Level $(if ($warningCount -gt 0) { 'Warning' } else { 'Info' }) Write-BuildMessage " Information: $infoCount" -Level Info if ($analyzerResults.Count -gt 0) { $analyzerResults | Format-Table -AutoSize } if ($errorCount -gt 0) { Write-BuildMessage "PSScriptAnalyzer found $errorCount errors that must be fixed" -Level Error throw "Build failed due to PSScriptAnalyzer errors" } if ($warningCount -gt 0) { Write-BuildMessage "PSScriptAnalyzer found $warningCount warnings" -Level Warning } } else { Write-BuildMessage "PSScriptAnalyzer completed with no issues found" -Level Success } } function Invoke-Test { Write-BuildMessage " === = TEST TASK === = " -Level Info if (-not (Test-Path $BuildVariables.TestPath)) { Write-BuildMessage "Test directory not found: $($BuildVariables.TestPath)" -Level Warning Write-BuildMessage "Skipping tests" -Level Warning return } Write-BuildMessage "Running Pester tests..." # Ensure Output directory exists if (-not (Test-Path $BuildVariables.OutputPath)) { New-Item -Path $BuildVariables.OutputPath -ItemType Directory -Force | Out-Null } # Import the module first for testing try { Import-Module $BuildVariables.ManifestPath -Force Write-BuildMessage "Module imported successfully for testing" -Level Success } catch { Write-BuildMessage "Failed to import module for testing: $($_.Exception.Message)" -Level Error throw } $testOutputFile = Join-Path $BuildVariables.OutputPath 'TestResults.xml' # Run Pester tests with proper configuration $pesterConfig = @{ Path = $BuildVariables.TestPath OutputFormat = 'NUnitXml' OutputFile = $testOutputFile PassThru = $true Verbose = $false } $testResults = Invoke-Pester @pesterConfig Write-BuildMessage "Test Results:" -Level Info Write-BuildMessage " Total: $($testResults.TotalCount)" -Level Info Write-BuildMessage " Passed: $($testResults.PassedCount)" -Level Success Write-BuildMessage " Failed: $($testResults.FailedCount)" -Level $(if ($testResults.FailedCount -gt 0) { 'Error' } else { 'Success' }) Write-BuildMessage " Skipped: $($testResults.SkippedCount)" -Level Info if ($testResults.FailedCount -gt 0) { Write-BuildMessage "Pester tests failed: $($testResults.FailedCount) out of $($testResults.TotalCount) tests failed" -Level Error throw "Build failed due to test failures" } Write-BuildMessage "All tests passed successfully!" -Level Success } function Invoke-Build { Write-BuildMessage " === = BUILD TASK === = " -Level Info Write-BuildMessage "Building module package..." $moduleOutputPath = Join-Path $BuildVariables.OutputPath $BuildVariables.ModuleName if (Test-Path $moduleOutputPath) { Remove-Item $moduleOutputPath -Recurse -Force } New-Item -Path $moduleOutputPath -ItemType Directory -Force | Out-Null # Copy core module files Copy-Item -Path $BuildVariables.ModulePath -Destination $moduleOutputPath Copy-Item -Path $BuildVariables.ManifestPath -Destination $moduleOutputPath Write-BuildMessage "Copied core module files" -Level Success # Copy folders $foldersToInclude = @('Public', 'Private', 'docs') foreach ($folder in $foldersToInclude) { $sourcePath = Join-Path $BuildVariables.SourcePath $folder if (Test-Path $sourcePath) { $destinationPath = Join-Path $moduleOutputPath $folder Copy-Item -Path $sourcePath -Destination $destinationPath -Recurse -Force Write-BuildMessage "Copied $folder folder" -Level Success } } # Copy additional files $additionalFiles = @('README.md', 'CHANGELOG.md', 'LICENSE') foreach ($file in $additionalFiles) { $sourcePath = Join-Path $BuildVariables.SourcePath $file if (Test-Path $sourcePath) { Copy-Item -Path $sourcePath -Destination $moduleOutputPath Write-BuildMessage "Copied $file" -Level Success } } Write-BuildMessage "Module built successfully at: $moduleOutputPath" -Level Success # Validate the built module try { $builtManifest = Test-ModuleManifest -Path (Join-Path $moduleOutputPath "$($BuildVariables.ModuleName).psd1") Write-BuildMessage "Built module validation passed" -Level Success Write-BuildMessage " Module: $($builtManifest.Name)" -Level Info Write-BuildMessage " Version: $($builtManifest.Version)" -Level Info Write-BuildMessage " Functions: $($builtManifest.ExportedFunctions.Count)" -Level Info } catch { Write-BuildMessage "Built module validation failed: $($_.Exception.Message)" -Level Error throw } } function Invoke-Publish { Write-BuildMessage " === = PUBLISH TASK === = " -Level Info $moduleOutputPath = Join-Path $BuildVariables.OutputPath $BuildVariables.ModuleName if (-not (Test-Path $moduleOutputPath)) { Write-BuildMessage "Module package not found. Run Build task first." -Level Error throw "Module package not found at: $moduleOutputPath" } # Validate API key if (-not $env:POWERSHELL_GALLERY_API_KEY) { Write-BuildMessage "PowerShell Gallery API key not found" -Level Error Write-BuildMessage "Set the POWERSHELL_GALLERY_API_KEY environment variable" -Level Error throw "PowerShell Gallery API key not found" } Write-BuildMessage "Publishing module to PowerShell Gallery..." try { Publish-Module -Path $moduleOutputPath -NuGetApiKey $env:POWERSHELL_GALLERY_API_KEY -Verbose Write-BuildMessage "Module published successfully to PowerShell Gallery!" -Level Success } catch { Write-BuildMessage "Failed to publish module: $($_.Exception.Message)" -Level Error throw } } # Main execution try { Write-BuildMessage "Starting build process for $($BuildVariables.ModuleName)" -Level Info Write-BuildMessage "Configuration: $Configuration" -Level Info Write-BuildMessage "Task: $Task" -Level Info # Install required modules first Install-RequiredModule # Execute tasks based on the requested task switch ($Task) { 'Clean' { Invoke-Clean } 'Analyze' { Invoke-Analyze } 'Test' { Invoke-Analyze Invoke-Test } 'Build' { Invoke-Clean Invoke-Analyze Invoke-Test Invoke-Build } 'Publish' { Invoke-Clean Invoke-Analyze Invoke-Test Invoke-Build Invoke-Publish } 'Default' { Invoke-Clean Invoke-Analyze Invoke-Test } default { Write-BuildMessage "Unknown task: $Task" -Level Error throw "Unknown task specified" } } Write-BuildMessage "Build completed successfully!" -Level Success } catch { Write-BuildMessage "Build failed: $($_.Exception.Message)" -Level Error exit 1 } |