Skip to content

Add consistent try/catch error handling to script main execution blocks #289

@WilliamBerryiii

Description

@WilliamBerryiii

Summary

Wrap main execution logic in all production scripts with consistent try/catch blocks to ensure proper error reporting and exit codes in CI pipelines.

Problem

Most scripts lack top-level error handling, causing:

  • Unhandled exceptions to produce verbose stack traces instead of actionable messages
  • Inconsistent exit codes when errors occur
  • CI pipelines failing without clear indication of what went wrong

Current state audit:

Script Has try/catch wrapper Exit code handling
linting/Invoke-PSScriptAnalyzer.ps1 Partial (exits on specific conditions)
linting/Invoke-YamlLint.ps1 Partial
linting/Invoke-LinkLanguageCheck.ps1
linting/Link-Lang-Check.ps1
linting/Validate-MarkdownFrontmatter.ps1 Partial
linting/Markdown-Link-Check.ps1 Partial
security/Test-DependencyPinning.ps1 Partial (line 860+)
security/Test-SHAStaleness.ps1 Partial
security/Update-ActionSHAPinning.ps1
extension/Package-Extension.ps1 Partial
extension/Prepare-Extension.ps1
lib/Get-VerifiedDownload.ps1
dev-tools/Generate-PrReference.ps1

Solution

Add a standardized try/catch wrapper pattern to each script's main execution block.

Pattern Template

#region Main Execution

try {
    # Existing script logic here
    # ...
    
    # Success exit
    exit 0
}
catch {
    Write-Error "Script failed: $($_.Exception.Message)"
    Write-Error "Stack trace: $($_.ScriptStackTrace)"
    exit 1
}

#endregion

Example: Before (Invoke-LinkLanguageCheck.ps1)

# Run the language check script
$scriptArgs = @{}
if ($ExcludePaths.Count -gt 0) {
    $scriptArgs['ExcludePaths'] = $ExcludePaths
}
$jsonOutput = & (Join-Path $PSScriptRoot "Link-Lang-Check.ps1") @scriptArgs 2>&1

try {
    $results = $jsonOutput | ConvertFrom-Json
    # ... rest of logic
}
catch {
    Write-Warning "Could not parse output as JSON: $jsonOutput"
}
# Script ends without explicit exit

Example: After (Invoke-LinkLanguageCheck.ps1)

#region Main Execution

try {
    # Run the language check script
    $scriptArgs = @{}
    if ($ExcludePaths.Count -gt 0) {
        $scriptArgs['ExcludePaths'] = $ExcludePaths
    }
    $jsonOutput = & (Join-Path $PSScriptRoot "Link-Lang-Check.ps1") @scriptArgs 2>&1

    try {
        $results = $jsonOutput | ConvertFrom-Json
        # ... rest of logic
    }
    catch {
        Write-Warning "Could not parse output as JSON: $jsonOutput"
    }
    
    exit 0
}
catch {
    Write-Error "Link language check failed: $($_.Exception.Message)"
    if ($env:GITHUB_ACTIONS -eq 'true') {
        Write-Output "::error::$($_.Exception.Message)"
    }
    exit 1
}

#endregion

Implementation Tasks

For each of the 13 scripts:

  1. Identify the main execution block (code after function definitions and module imports)
  2. Wrap in try { } catch { } block
  3. Add #region Main Execution / #endregion markers
  4. Ensure explicit exit 0 on success path
  5. Add exit 1 in catch block
  6. Include CI-friendly error output in catch block

Scripts requiring significant refactoring:

  • scripts/security/Test-DependencyPinning.ps1 - Main logic starts around line 825
  • scripts/security/Test-SHAStaleness.ps1 - Main logic starts around line 720
  • scripts/security/Update-ActionSHAPinning.ps1 - Main logic starts around line 450

Scripts with simpler main blocks:

  • scripts/linting/Invoke-*.ps1 - Main logic is most of the file after imports
  • scripts/extension/*.ps1 - Clearly delineated main sections

Validation

Test error handling by introducing intentional failures:

# Test that errors produce exit code 1
./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -ConfigPath "nonexistent.psd1"
$LASTEXITCODE  # Should be 1

# Test that success produces exit code 0
./scripts/linting/Invoke-PSScriptAnalyzer.ps1
$LASTEXITCODE  # Should be 0

Acceptance Criteria

  • All 13 production scripts have try/catch wrappers around main execution
  • All scripts use exit 0 for success and exit 1 for failure
  • Error messages are concise and actionable (not full stack traces)
  • CI-specific error annotations are included where appropriate
  • #region Main Execution markers delineate the wrapped code
  • Existing functionality is preserved (no behavior changes on success path)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions