Simulate-ADOCodeReview.ps1

<#
.SYNOPSIS
Simulates Azure DevOps AI Code Review Pipeline
 
.DESCRIPTION
This script simulates how the AI Code Review module will work in an Azure DevOps pipeline:
1. Detects changed AL files in the current branch
2. Performs AI code review using configured provider
3. Formats output in Azure DevOps format (##vso commands)
4. Shows summary with warnings/errors
5. Sets pipeline result (success/warning/failure)
 
Run this in any git repository with AL files to see how the pipeline will behave.
 
.PARAMETER BaseBranch
The base branch to compare against (default: origin/master)
 
.PARAMETER Provider
AI provider to use: github, azure, anthropic, openai (default: github)
 
.PARAMETER Model
AI model to use (default: gpt-4o)
 
.PARAMETER FailOnViolations
If set, the script will exit with error code 1 if violations are found
 
.PARAMETER Verbose
Show detailed execution information
 
.EXAMPLE
# Basic usage - review changes against master
.\Simulate-ADOCodeReview.ps1
 
.EXAMPLE
# Review against different branch
.\Simulate-ADOCodeReview.ps1 -BaseBranch "origin/main"
 
.EXAMPLE
# Use different AI provider
.\Simulate-ADOCodeReview.ps1 -Provider "azure" -Model "gpt-4"
 
.EXAMPLE
# Fail pipeline if violations found (strict mode)
.\Simulate-ADOCodeReview.ps1 -FailOnViolations
 
.NOTES
Author: waldo
Version: 1.0.0
Requires: iFacto.AICodeReview module installed
#>


[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [string]$BaseBranch = "origin/master",
    
    [Parameter(Mandatory = $false)]
    [ValidateSet("github", "azure", "anthropic", "openai")]
    [string]$Provider = "github",
    
    [Parameter(Mandatory = $false)]
    [string]$ConfigPath,
    
    [Parameter(Mandatory = $false)]
    [string]$RulesPath,
    
    [Parameter(Mandatory = $false)]
    [switch]$FailOnViolations,
    
    [Parameter(Mandatory = $false)]
    [switch]$VerboseOutput
)

# Pipeline simulation colors
$script:ADOColors = @{
    Section = "Cyan"
    Success = "Green"
    Warning = "Yellow"
    Error = "Red"
    Info = "White"
    Debug = "Gray"
}

function Write-ADOSection {
    param([string]$Message)
    Write-Host ""
    Write-Host "##[section]$Message" -ForegroundColor $script:ADOColors.Section
}

function Write-ADOCommand {
    param([string]$Message)
    Write-Host "##[command]$Message" -ForegroundColor $script:ADOColors.Debug
}

function Write-ADOWarning {
    param([string]$Message)
    Write-Host "##vso[task.logissue type=warning]$Message" -ForegroundColor $script:ADOColors.Warning
}

function Write-ADOError {
    param([string]$Message)
    Write-Host "##vso[task.logissue type=error]$Message" -ForegroundColor $script:ADOColors.Error
}

function Write-ADOSuccess {
    param([string]$Message)
    Write-Host "$Message" -ForegroundColor $script:ADOColors.Success
}

function Write-ADOInfo {
    param([string]$Message)
    Write-Host "$Message" -ForegroundColor $script:ADOColors.Info
}

# Banner
Clear-Host
Write-Host "╔════════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host "║ ║" -ForegroundColor Cyan
Write-Host "║ Azure DevOps - AI Code Review Simulation ║" -ForegroundColor Cyan
Write-Host "║ ║" -ForegroundColor Cyan
Write-Host "╚════════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
Write-Host ""

# Simulate pipeline start
$buildId = Get-Random -Minimum 10000 -Maximum 99999
$startTime = Get-Date
Write-ADOInfo "Build: #$buildId"
Write-ADOInfo "Started: $($startTime.ToString('yyyy-MM-dd HH:mm:ss'))"
Write-ADOInfo "Agent: Simulation ($(hostname))"
Write-Host ""

# Step 1: Environment Check
Write-ADOSection "Task: Check Environment"
Write-ADOCommand "Checking prerequisites..."

# Check for module
try {
    $module = Get-Module -Name "iFacto.AICodeReview" -ListAvailable | Select-Object -First 1
    if (-not $module) {
        Write-ADOError "Module 'iFacto.AICodeReview' not found. Please install: Install-Module iFacto.AICodeReview"
        exit 1
    }
    Write-ADOSuccess "✓ Module found: iFacto.AICodeReview v$($module.Version)"
    
    # Import module
    Import-Module iFacto.AICodeReview -Force
    Write-ADOSuccess "✓ Module imported"
} catch {
    Write-ADOError "Failed to load module: $_"
    exit 1
}

# Check for git repository
if (-not (Test-Path ".git")) {
    Write-ADOError "Not a git repository. Please run this script from a git repository root."
    exit 1
}
Write-ADOSuccess "✓ Git repository detected"

# Check for API key (based on provider)
$apiKeyVar = switch ($Provider) {
    "github" { "GITHUB_TOKEN" }
    "azure" { "AZURE_OPENAI_API_KEY" }
    "anthropic" { "ANTHROPIC_API_KEY" }
    "openai" { "OPENAI_API_KEY" }
}

if (-not (Get-Item "env:$apiKeyVar" -ErrorAction SilentlyContinue)) {
    Write-ADOWarning "Environment variable '$apiKeyVar' not set. AI review will fail."
    Write-ADOInfo "Set the API key: `$env:$apiKeyVar = 'your-key-here'"
} else {
    $keyLength = (Get-Item "env:$apiKeyVar").Value.Length
    Write-ADOSuccess "✓ API key configured ($keyLength chars)"
}

# Get git info
$currentBranch = git rev-parse --abbrev-ref HEAD
$currentCommit = git rev-parse --short HEAD
Write-ADOInfo "Branch: $currentBranch"
Write-ADOInfo "Commit: $currentCommit"
Write-ADOInfo "Base: $BaseBranch"

# Step 2: Detect Changes
Write-ADOSection "Task: Detect Changed AL Files"
Write-ADOCommand "git fetch origin"
git fetch origin 2>&1 | ForEach-Object { Write-Host " $_" -ForegroundColor Gray }

Write-ADOCommand "Get-ChangedALFiles -BaseBranch '$BaseBranch'"
try {
    $changedFiles = Get-ChangedALFiles -BaseBranch $BaseBranch
    
    if ($changedFiles.Count -eq 0) {
        Write-ADOWarning "No AL files changed. Skipping review."
        Write-ADOSuccess "✓ Task completed: No changes to review"
        exit 0
    }
    
    Write-ADOSuccess "✓ Found $($changedFiles.Count) changed AL file(s):"
    foreach ($file in $changedFiles) {
        Write-ADOInfo " - $file"
    }
} catch {
    Write-ADOError "Failed to detect changes: $_"
    exit 1
}

# Step 3: AI Code Review
Write-ADOSection "Task: AI Code Review"
Write-ADOInfo "Provider: $Provider"
if ($ConfigPath) {
    Write-ADOInfo "Config: $ConfigPath"
}
if ($RulesPath) {
    Write-ADOInfo "Rules: $RulesPath"
}
Write-ADOInfo "Files to review: $($changedFiles.Count)"
Write-Host ""

$invokeParams = @{
    BaseBranch = $BaseBranch
    Provider = $Provider
}
if ($ConfigPath) { $invokeParams.ConfigPath = $ConfigPath }
if ($RulesPath) { $invokeParams.RulesPath = $RulesPath }

$cmdText = "Invoke-AICodeReview -BaseBranch '$BaseBranch' -Provider '$Provider'"
if ($ConfigPath) { $cmdText += " -ConfigPath '$ConfigPath'" }
if ($RulesPath) { $cmdText += " -RulesPath '$RulesPath'" }
Write-ADOCommand $cmdText

try {
    # Capture start time
    $reviewStart = Get-Date
    
    # Call the AI review
    $reviewResult = Invoke-AICodeReview @invokeParams
    
    $reviewDuration = ((Get-Date) - $reviewStart).TotalSeconds
    Write-Host ""
    Write-ADOInfo "Review completed in $([math]::Round($reviewDuration, 1)) seconds"
    
} catch {
    Write-ADOError "AI Code Review failed: $_"
    Write-ADOError $_.ScriptStackTrace
    exit 1
}

# Step 4: Process Results
Write-ADOSection "Task: Process Review Results"

if (-not $reviewResult -or $reviewResult.Count -eq 0) {
    Write-ADOSuccess "✓ No violations found"
    Write-ADOSuccess "✓ Code review passed"
    
    # Summary
    Write-ADOSection "Summary"
    Write-ADOSuccess "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    Write-ADOSuccess " BUILD SUCCEEDED"
    Write-ADOSuccess "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    Write-ADOInfo "Files reviewed: $($changedFiles.Count)"
    Write-ADOSuccess "Violations: 0"
    $duration = ((Get-Date) - $startTime).TotalSeconds
    Write-ADOInfo "Duration: $([math]::Round($duration, 1)) seconds"
    Write-Host ""
    
    exit 0
}

# Parse violations and format for ADO
Write-ADOCommand "Parsing violations..."
$violations = @()
$warningCount = 0
$errorCount = 0

foreach ($file in $reviewResult.PSObject.Properties.Name) {
    $fileViolations = $reviewResult.$file
    
    foreach ($violation in $fileViolations) {
        $severity = if ($violation.severity) { $violation.severity } else { "warning" }
        $message = if ($violation.message) { $violation.message } else { $violation.ToString() }
        $line = if ($violation.line) { $violation.line } else { "1" }
        
        # Create violation object for the array
        $violationObj = [PSCustomObject]@{
            file = $file
            line = $line
            severity = $severity
            message = $message
        }
        
        $violations += $violationObj
        
        # Count by severity
        if ($severity -eq "error") {
            $errorCount++
        } else {
            $warningCount++
        }
    }
}

# Convert all violations to ADO format at once
if ($violations.Count -gt 0) {
    $adoOutput = ConvertTo-ADOLogFormat -Violations $violations
    Write-Host $adoOutput
}

Write-Host ""
Write-ADOInfo "Total violations: $($violations.Count)"
Write-ADOInfo "Errors: $errorCount"
Write-ADOInfo "Warnings: $warningCount"

# Create summary table
Write-Host ""
Write-ADOInfo "Violation Details:"
Write-Host ""
$violations | Format-Table -Property @{
    Label = "File"
    Expression = { [System.IO.Path]::GetFileName($_.File) }
    Width = 30
}, @{
    Label = "Line"
    Expression = { $_.Line }
    Width = 6
}, @{
    Label = "Severity"
    Expression = { $_.Severity.ToUpper() }
    Width = 10
}, @{
    Label = "Message"
    Expression = { 
        if ($_.Message.Length -gt 60) {
            $_.Message.Substring(0, 57) + "..."
        } else {
            $_.Message
        }
    }
    Width = 60
} -AutoSize

# Step 5: Set Pipeline Result
Write-ADOSection "Task: Set Pipeline Result"

$duration = ((Get-Date) - $startTime).TotalSeconds

if ($errorCount -gt 0) {
    Write-ADOError "Build completed with $errorCount error(s)"
    
    if ($FailOnViolations) {
        Write-Host "##vso[task.complete result=Failed;]AI Code Review found critical violations"
        
        Write-Host ""
        Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Red
        Write-Host " BUILD FAILED" -ForegroundColor Red
        Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Red
        Write-ADOInfo "Files reviewed: $($changedFiles.Count)"
        Write-ADOError "Errors: $errorCount"
        Write-ADOWarning "Warnings: $warningCount"
        Write-ADOInfo "Duration: $([math]::Round($duration, 1)) seconds"
        Write-Host ""
        
        exit 1
    } else {
        Write-Host "##vso[task.complete result=SucceededWithIssues;]AI Code Review found violations (warnings only)"
        
        Write-Host ""
        Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Yellow
        Write-Host " BUILD SUCCEEDED WITH ISSUES" -ForegroundColor Yellow
        Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Yellow
        Write-ADOInfo "Files reviewed: $($changedFiles.Count)"
        Write-ADOError "Errors: $errorCount"
        Write-ADOWarning "Warnings: $warningCount"
        Write-ADOInfo "Duration: $([math]::Round($duration, 1)) seconds"
        Write-Host ""
        
        exit 0
    }
} elseif ($warningCount -gt 0) {
    Write-ADOWarning "Build completed with $warningCount warning(s)"
    Write-Host "##vso[task.complete result=SucceededWithIssues;]AI Code Review found warnings"
    
    Write-Host ""
    Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Yellow
    Write-Host " BUILD SUCCEEDED WITH ISSUES" -ForegroundColor Yellow
    Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Yellow
    Write-ADOInfo "Files reviewed: $($changedFiles.Count)"
    Write-ADOWarning "Warnings: $warningCount"
    Write-ADOInfo "Duration: $([math]::Round($duration, 1)) seconds"
    Write-Host ""
    
    exit 0
} else {
    Write-ADOSuccess "✓ Build succeeded with no issues"
    Write-Host "##vso[task.complete result=Succeeded;]AI Code Review passed"
    
    Write-Host ""
    Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Green
    Write-Host " BUILD SUCCEEDED" -ForegroundColor Green
    Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Green
    Write-ADOInfo "Files reviewed: $($changedFiles.Count)"
    Write-ADOSuccess "Violations: 0"
    Write-ADOInfo "Duration: $([math]::Round($duration, 1)) seconds"
    Write-Host ""
    
    exit 0
}