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 } |