#!/usr/bin/env pwsh <# .SYNOPSIS Anonymous Apex execution wrapper for Salesforce CLI .DESCRIPTION A user-friendly wrapper around 'sf apex run' that simplifies executing anonymous Apex code from files or inline strings with better formatting and error handling. .PARAMETER File Path to Apex file to execute .PARAMETER Code Inline Apex code to execute (alternative to -File) .PARAMETER o Target org username or alias (uses default if not specified) .PARAMETER Verbose Enable verbose output showing execution details .PARAMETER Help Show this help message .EXAMPLE .\sf-apex-run.ps1 -File "scripts/setup.apex" .\sf-apex-run.ps1 -Code "System.debug('Hello World');" .\sf-apex-run.ps1 -File "test.apex" -o "sandbox" .\sf-apex-run.ps1 -Code "Database.insert(new Account(Name='Test'));" -Verbose .NOTES This script automatically checks for Salesforce CLI installation and runs diagnostics if the CLI is not found. #> param( [Parameter(ParameterSetName="File")] [string]$File, [Parameter(ParameterSetName="Code")] [string]$Code, [string]$o, [switch]$Verbose, [switch]$Help ) # Show help if requested if ($Help) { Get-Help $MyInvocation.MyCommand.Path -Detailed exit 0 } # Function to check if Salesforce CLI is installed function Test-SalesforceCLI { try { $null = Get-Command sf -ErrorAction Stop return $true } catch { return $false } } # Function to run sf-check diagnostics function Invoke-SalesforceCheck { $checkScript = if (Test-Path "sf-check.ps1") { ".\sf-check.ps1" } elseif (Test-Path "sf-check.sh") { "bash sf-check.sh" } else { $null } if ($checkScript) { Write-Host "Running Salesforce CLI diagnostics..." -ForegroundColor Yellow Invoke-Expression $checkScript } else { Write-Host "Salesforce CLI not found and no diagnostic script available." -ForegroundColor Red Write-Host "Please install the Salesforce CLI: https://developer.salesforce.com/tools/salesforcecli" -ForegroundColor Red } } # Function to display code preview function Show-CodePreview { param([string]$CodeContent, [string]$Source) Write-Host "📝 Apex Code ($Source):" -ForegroundColor Yellow Write-Host "----------------------------------------" -ForegroundColor Gray # Show first few lines of code for preview $lines = $CodeContent -split "`n" $previewLines = if ($lines.Count -gt 10) { $lines[0..9] + @("... (truncated, $($lines.Count - 10) more lines)") } else { $lines } foreach ($line in $previewLines) { Write-Host " $line" -ForegroundColor White } Write-Host "----------------------------------------" -ForegroundColor Gray } # Silently check for Salesforce CLI if (-not (Test-SalesforceCLI)) { Invoke-SalesforceCheck exit 1 } # Validate that either file or code is provided if (-not $File -and -not $Code) { Write-Host "Error: Must specify either -File or -Code parameter" -ForegroundColor Red Write-Host "" Write-Host "Usage examples:" -ForegroundColor Yellow Write-Host " .\sf-apex-run.ps1 -File `"scripts/setup.apex`"" -ForegroundColor Gray Write-Host " .\sf-apex-run.ps1 -Code `"System.debug('Hello World');`"" -ForegroundColor Gray Write-Host "" Write-Host "Use -Help for detailed usage information." -ForegroundColor Yellow exit 1 } # Validate that both file and code aren't provided if ($File -and $Code) { Write-Host "Error: Cannot specify both -File and -Code parameters" -ForegroundColor Red exit 1 } # If file is specified, validate it exists and read content if ($File) { if (-not (Test-Path $File)) { Write-Host "Error: Apex file not found: $File" -ForegroundColor Red exit 1 } try { $apexContent = Get-Content -Path $File -Raw Write-Host "Using Apex file: $File" -ForegroundColor Green if ($Verbose) { Show-CodePreview $apexContent "from file: $File" } } catch { Write-Host "Error reading Apex file: $($_.Exception.Message)" -ForegroundColor Red exit 1 } } else { $apexContent = $Code Write-Host "Using inline Apex code" -ForegroundColor Green if ($Verbose) { Show-CodePreview $apexContent "inline" } } # Build the sf command $sfArgs = @("apex", "run") # Add target org if specified if ($o) { $sfArgs += "--target-org" $sfArgs += $o Write-Host "Target org: $o" -ForegroundColor Cyan } # Add verbose flag if requested if ($Verbose) { $sfArgs += "--verbose" } # Display execution info Write-Host "" Write-Host "🚀 Executing Anonymous Apex" -ForegroundColor Blue Write-Host "============================" -ForegroundColor Blue # Create a temporary file for the Apex content if needed $tempFile = $null if ($Code) { $tempFile = [System.IO.Path]::GetTempFileName() + ".apex" try { Set-Content -Path $tempFile -Value $apexContent -Encoding UTF8 $sfArgs += "--file" $sfArgs += $tempFile } catch { Write-Host "Error creating temporary file: $($_.Exception.Message)" -ForegroundColor Red exit 1 } } else { $sfArgs += "--file" $sfArgs += $File } # Display the command being run (without showing temp file path) $displayArgs = $sfArgs -replace [regex]::Escape($tempFile), '' Write-Host "" Write-Host "Executing: sf $($displayArgs -join ' ')" -ForegroundColor Gray Write-Host "" # Execute the command try { $startTime = Get-Date & sf @sfArgs $exitCode = $LASTEXITCODE $endTime = Get-Date $duration = $endTime - $startTime Write-Host "" Write-Host "⏱️ Execution completed in $($duration.TotalSeconds.ToString('F2')) seconds" -ForegroundColor Gray if ($exitCode -eq 0) { Write-Host "" Write-Host "✅ Anonymous Apex executed successfully!" -ForegroundColor Green if ($Verbose) { Write-Host "💡 Check the output above for any System.debug() statements" -ForegroundColor Yellow } } else { Write-Host "" Write-Host "❌ Apex execution failed with exit code: $exitCode" -ForegroundColor Red Write-Host "💡 Check compilation errors or runtime exceptions above" -ForegroundColor Yellow # Clean up temp file before exiting if ($tempFile -and (Test-Path $tempFile)) { Remove-Item $tempFile -Force -ErrorAction SilentlyContinue } exit $exitCode } } catch { Write-Host "Error executing sf command: $($_.Exception.Message)" -ForegroundColor Red # Clean up temp file before exiting if ($tempFile -and (Test-Path $tempFile)) { Remove-Item $tempFile -Force -ErrorAction SilentlyContinue } exit 1 } finally { # Clean up temporary file if ($tempFile -and (Test-Path $tempFile)) { Remove-Item $tempFile -Force -ErrorAction SilentlyContinue } }