Build/Build-Module.ps1

<#
.SYNOPSIS
Build and validate the iFacto.AICodeReview module
 
.DESCRIPTION
This script validates the module structure, runs tests, and checks the manifest.
Use this before publishing to PowerShell Gallery.
 
.PARAMETER SkipTests
Skip running Pester tests
 
.EXAMPLE
.\Build-Module.ps1
 
.EXAMPLE
.\Build-Module.ps1 -SkipTests
#>

[CmdletBinding()]
param(
    [Parameter()]
    [switch]$SkipTests
)

$ErrorActionPreference = 'Stop'

# Get module root directory
$moduleRoot = Split-Path $PSScriptRoot -Parent
$moduleName = 'iFacto.AICodeReview'
$manifestPath = Join-Path $moduleRoot "$moduleName.psd1"

Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Building $moduleName Module" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""

# Step 1: Validate module structure
Write-Host "✓ Validating module structure..." -ForegroundColor Yellow

$requiredFolders = @('Public', 'Private', 'Rules', 'Tests', 'Examples')
foreach ($folder in $requiredFolders) {
    $folderPath = Join-Path $moduleRoot $folder
    if (-not (Test-Path $folderPath)) {
        throw "Required folder missing: $folder"
    }
    Write-Host " ✓ $folder" -ForegroundColor Green
}

# Step 2: Validate manifest
Write-Host ""
Write-Host "✓ Validating module manifest..." -ForegroundColor Yellow

try {
    $manifest = Test-ModuleManifest -Path $manifestPath -ErrorAction Stop
    Write-Host " ✓ Manifest valid" -ForegroundColor Green
    Write-Host " ✓ Version: $($manifest.Version)" -ForegroundColor Green
    Write-Host " ✓ Author: $($manifest.Author)" -ForegroundColor Green
    Write-Host " ✓ Functions: $($manifest.ExportedFunctions.Count)" -ForegroundColor Green
}
catch {
    throw "Module manifest validation failed: $_"
}

# Step 3: Check function exports match actual files
Write-Host ""
Write-Host "✓ Validating function exports..." -ForegroundColor Yellow

$publicFunctions = Get-ChildItem -Path (Join-Path $moduleRoot 'Public') -Filter '*.ps1' | 
    Select-Object -ExpandProperty BaseName

$exportedFunctions = $manifest.ExportedFunctions.Keys

foreach ($func in $publicFunctions) {
    if ($func -notin $exportedFunctions) {
        throw "Function '$func' exists in Public but not exported in manifest"
    }
    Write-Host " ✓ $func" -ForegroundColor Green
}

foreach ($func in $exportedFunctions) {
    if ($func -notin $publicFunctions) {
        throw "Function '$func' exported in manifest but file missing in Public"
    }
}

# Step 4: Run Pester tests
if (-not $SkipTests) {
    Write-Host ""
    Write-Host "✓ Running Pester tests..." -ForegroundColor Yellow
    
    # Import Pester
    $pesterModule = Get-Module Pester -ListAvailable | Where-Object { $_.Version -ge '5.0' } | Select-Object -First 1
    if (-not $pesterModule) {
        throw "Pester 5.x or higher is required. Install with: Install-Module Pester -Force -SkipPublisherCheck"
    }
    Import-Module Pester -MinimumVersion 5.0 -Force
    
    # Run tests
    $testsPath = Join-Path $moduleRoot 'Tests'
    $testResults = Invoke-Pester -Path $testsPath -PassThru
    
    if ($testResults.FailedCount -gt 0) {
        throw "Tests failed: $($testResults.FailedCount) test(s) failed"
    }
    
    Write-Host " ✓ All tests passed ($($testResults.PassedCount) tests)" -ForegroundColor Green
}
else {
    Write-Host ""
    Write-Host "⚠ Skipping tests" -ForegroundColor Yellow
}

# Step 5: Validate required files exist
Write-Host ""
Write-Host "✓ Validating required files..." -ForegroundColor Yellow

$requiredFiles = @(
    'README.md',
    'LICENSE',
    'CHANGELOG.md',
    'Rules\ifacto-company-rules.md',
    'Rules\model-config.json'
)

foreach ($file in $requiredFiles) {
    $filePath = Join-Path $moduleRoot $file
    if (-not (Test-Path $filePath)) {
        throw "Required file missing: $file"
    }
    Write-Host " ✓ $file" -ForegroundColor Green
}

# Step 6: Check for syntax errors in all .ps1 files
Write-Host ""
Write-Host "✓ Checking PowerShell syntax..." -ForegroundColor Yellow

$psFiles = Get-ChildItem -Path $moduleRoot -Filter '*.ps1' -Recurse -Exclude '*.Tests.ps1'
$syntaxErrors = @()

foreach ($file in $psFiles) {
    $errors = $null
    [System.Management.Automation.PSParser]::Tokenize((Get-Content -Path $file.FullName -Raw), [ref]$errors) | Out-Null
    
    if ($errors.Count -gt 0) {
        $syntaxErrors += @{
            File = $file.FullName
            Errors = $errors
        }
    }
}

if ($syntaxErrors.Count -gt 0) {
    Write-Host ""
    Write-Host "Syntax errors found:" -ForegroundColor Red
    foreach ($item in $syntaxErrors) {
        Write-Host " File: $($item.File)" -ForegroundColor Red
        foreach ($error in $item.Errors) {
            Write-Host " $error" -ForegroundColor Red
        }
    }
    throw "Syntax errors found in PowerShell files"
}

Write-Host " ✓ No syntax errors" -ForegroundColor Green

# Success
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host " ✓ Build Complete - Module is valid!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host "Module is ready to publish:" -ForegroundColor Cyan
Write-Host " Publish-Module -Path '$moduleRoot' -NuGetApiKey `$env:NUGET_API_KEY" -ForegroundColor White
Write-Host ""