microsoft/hve-core
Publicmirrored fromhttps://github.com/microsoft/hve-coreAvailable
scripts/linting/README.md
584lines · modecode
| 1 | --- |
| 2 | title: Linting Scripts |
| 3 | description: PowerShell scripts for code quality validation and documentation checks |
| 4 | author: HVE Core Team |
| 5 | ms.date: 2025-11-05 |
| 6 | ms.topic: reference |
| 7 | keywords: |
| 8 | - powershell |
| 9 | - linting |
| 10 | - validation |
| 11 | - code quality |
| 12 | - markdown |
| 13 | estimated_reading_time: 10 |
| 14 | --- |
| 15 | |
| 16 | This directory contains PowerShell scripts for validating code quality and documentation standards in the `hve-core` repository. |
| 17 | |
| 18 | ## Architecture |
| 19 | |
| 20 | The linting scripts follow a **modular architecture** with shared helper functions: |
| 21 | |
| 22 | * **Wrapper Scripts** (`Invoke-*.ps1`) - Entry points that orchestrate validation logic |
| 23 | * **Core Scripts** - Existing validation logic (e.g., `Link-Lang-Check.ps1`, `Validate-MarkdownFrontmatter.ps1`) |
| 24 | * **Shared Module** (`Modules/LintingHelpers.psm1`) - Common functions for file discovery and git operations |
| 25 | * **CI Helpers** (`scripts/lib/Modules/CIHelpers.psm1`) - CI annotations, outputs, env flags, and step summaries |
| 26 | * **Configuration Files** - Tool-specific settings (e.g., `PSScriptAnalyzer.psd1`, `markdown-link-check.config.json`) |
| 27 | |
| 28 | ## Scripts |
| 29 | |
| 30 | ### PowerShell Linting |
| 31 | |
| 32 | #### `Invoke-PSScriptAnalyzer.ps1` |
| 33 | |
| 34 | Static analysis for PowerShell scripts using PSScriptAnalyzer. |
| 35 | |
| 36 | **Purpose**: Enforce PowerShell best practices and detect common issues. |
| 37 | |
| 38 | **Features**: |
| 39 | |
| 40 | * Detects changed PowerShell files via Git |
| 41 | * Supports analyzing all files or changed files only |
| 42 | * Creates CI annotations for violations |
| 43 | * Exports JSON results and markdown summary |
| 44 | * Configurable via `PSScriptAnalyzer.psd1` |
| 45 | |
| 46 | **Parameters**: |
| 47 | |
| 48 | * `-ChangedFilesOnly` (switch) - Analyze only files changed in current branch |
| 49 | |
| 50 | **Usage**: |
| 51 | |
| 52 | ```powershell |
| 53 | # Analyze all PowerShell files |
| 54 | ./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -Verbose |
| 55 | |
| 56 | # Analyze only changed files |
| 57 | ./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -ChangedFilesOnly |
| 58 | |
| 59 | # View detailed output |
| 60 | ./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -Verbose -Debug |
| 61 | ``` |
| 62 | |
| 63 | **GitHub Actions Integration**: |
| 64 | |
| 65 | * Workflow: `.github/workflows/psscriptanalyzer.yml` |
| 66 | * Artifacts: `psscriptanalyzer-results` (JSON + markdown) |
| 67 | * Exit Code: Non-zero if violations found |
| 68 | |
| 69 | #### `PSScriptAnalyzer.psd1` |
| 70 | |
| 71 | Configuration file for PSScriptAnalyzer rules. |
| 72 | |
| 73 | **Enforced Rules**: |
| 74 | |
| 75 | * **Severity**: Error and Warning levels |
| 76 | * **Best Practices**: Avoid aliases, use approved verbs, singular nouns |
| 77 | * **Help**: Require comment-based help |
| 78 | * **Security**: Check for credentials in code |
| 79 | * **Performance**: Identify inefficient patterns |
| 80 | |
| 81 | **Excluded Rules**: |
| 82 | |
| 83 | * `PSAvoidUsingWriteHost` - Allowed for script output |
| 84 | |
| 85 | ### YAML Linting |
| 86 | |
| 87 | #### `Invoke-YamlLint.ps1` |
| 88 | |
| 89 | Static analysis for GitHub Actions workflow files using actionlint. |
| 90 | |
| 91 | **Purpose**: Validate GitHub Actions workflow YAML syntax and best practices. |
| 92 | |
| 93 | **Features**: |
| 94 | |
| 95 | * Validates `.github/workflows/*.yml` and `.yaml` files |
| 96 | * Detects changed workflow files via Git |
| 97 | * Supports analyzing all files or changed files only |
| 98 | * Creates CI annotations for violations |
| 99 | * Exports JSON results and markdown summary |
| 100 | * Configurable via `.github/actionlint.yaml` |
| 101 | |
| 102 | **Parameters**: |
| 103 | |
| 104 | * `-ChangedFilesOnly` (switch) - Analyze only files changed in current branch |
| 105 | * `-BaseBranch` (string) - Base branch for comparison (default: `origin/main`) |
| 106 | * `-OutputPath` (string) - Output path for JSON results (default: `logs/yaml-lint-results.json`) |
| 107 | |
| 108 | **Usage**: |
| 109 | |
| 110 | ```powershell |
| 111 | # Analyze all workflow files |
| 112 | ./scripts/linting/Invoke-YamlLint.ps1 -Verbose |
| 113 | |
| 114 | # Analyze only changed files |
| 115 | ./scripts/linting/Invoke-YamlLint.ps1 -ChangedFilesOnly |
| 116 | |
| 117 | # View detailed output |
| 118 | ./scripts/linting/Invoke-YamlLint.ps1 -Verbose -Debug |
| 119 | ``` |
| 120 | |
| 121 | **GitHub Actions Integration**: |
| 122 | |
| 123 | * Workflow: `.github/workflows/yaml-lint.yml` |
| 124 | * Configuration: `.github/actionlint.yaml` |
| 125 | * Artifacts: `yaml-lint-results` (JSON) |
| 126 | * Exit Code: Non-zero if violations found |
| 127 | |
| 128 | ### Markdown Validation |
| 129 | |
| 130 | #### `Validate-MarkdownFrontmatter.ps1` |
| 131 | |
| 132 | Validates YAML frontmatter and footer format in markdown files. |
| 133 | |
| 134 | **Purpose**: Ensure consistent metadata across documentation. |
| 135 | |
| 136 | **Features**: |
| 137 | |
| 138 | * Validates required frontmatter fields |
| 139 | * Checks footer format and copyright notice |
| 140 | * Supports changed files only mode |
| 141 | * Configurable warnings-as-errors |
| 142 | * Creates CI annotations for all issues |
| 143 | * Exports JSON results with detailed statistics |
| 144 | * Generates comprehensive step summary |
| 145 | |
| 146 | **Parameters**: |
| 147 | |
| 148 | * `-ChangedFilesOnly` (switch) - Validate only changed markdown files |
| 149 | * `-SkipFooterValidation` (switch) - Skip footer checks |
| 150 | * `-WarningsAsErrors` (switch) - Treat warnings as errors |
| 151 | |
| 152 | **Artifacts Generated**: |
| 153 | |
| 154 | * `logs/frontmatter-validation-results.json` - Complete validation results including: |
| 155 | * Timestamp and script name |
| 156 | * Summary statistics (total files, error/warning counts) |
| 157 | * Lists of all errors and warnings |
| 158 | |
| 159 | **Usage**: |
| 160 | |
| 161 | ```powershell |
| 162 | # Validate all markdown files |
| 163 | ./scripts/linting/Validate-MarkdownFrontmatter.ps1 |
| 164 | |
| 165 | # Validate only changed files |
| 166 | ./scripts/linting/Validate-MarkdownFrontmatter.ps1 -ChangedFilesOnly |
| 167 | |
| 168 | # Skip footer validation |
| 169 | ./scripts/linting/Validate-MarkdownFrontmatter.ps1 -SkipFooterValidation |
| 170 | ``` |
| 171 | |
| 172 | **GitHub Actions Integration**: |
| 173 | |
| 174 | * Workflow: `.github/workflows/frontmatter-validation.yml` |
| 175 | * Artifacts: `frontmatter-validation-results` (JSON) |
| 176 | * Annotations: Errors and warnings with file paths |
| 177 | * Exit Code: Non-zero if validation fails |
| 178 | |
| 179 | #### `Invoke-LinkLanguageCheck.ps1` |
| 180 | |
| 181 | Detects URLs with language paths (e.g., `/en-us/`) that should be removed. |
| 182 | |
| 183 | **Purpose**: Ensure language-agnostic URLs for better internationalization. |
| 184 | |
| 185 | **Features**: |
| 186 | |
| 187 | * Scans all markdown files recursively |
| 188 | * Calls `Link-Lang-Check.ps1` for detection logic |
| 189 | * Creates CI warning annotations |
| 190 | * Provides fix instructions in summary |
| 191 | |
| 192 | **Usage**: |
| 193 | |
| 194 | ```powershell |
| 195 | # Check all markdown files |
| 196 | ./scripts/linting/Invoke-LinkLanguageCheck.ps1 -Verbose |
| 197 | |
| 198 | # View detection details |
| 199 | ./scripts/linting/Invoke-LinkLanguageCheck.ps1 -Debug |
| 200 | ``` |
| 201 | |
| 202 | **GitHub Actions Integration**: |
| 203 | |
| 204 | * Workflow: `.github/workflows/link-lang-check.yml` |
| 205 | * Annotations: Warnings on files with language paths |
| 206 | * Artifacts: `link-lang-check-results` (JSON + markdown) |
| 207 | |
| 208 | #### `Link-Lang-Check.ps1` |
| 209 | |
| 210 | Core logic for detecting language paths in URLs. |
| 211 | |
| 212 | **Detection Pattern**: Matches `/[a-z]{2}-[a-z]{2}/` patterns in Microsoft domain URLs. |
| 213 | |
| 214 | #### `Markdown-Link-Check.ps1` |
| 215 | |
| 216 | Validates all links in markdown files using markdown-link-check npm package. |
| 217 | |
| 218 | **Purpose**: Detect broken links before deployment. |
| 219 | |
| 220 | **Features**: |
| 221 | |
| 222 | * Checks internal and external links |
| 223 | * Configurable via `markdown-link-check.config.json` |
| 224 | * Retries failed links |
| 225 | * Respects robots.txt |
| 226 | * Creates CI annotations for broken links |
| 227 | * Exports JSON results with link statistics |
| 228 | * Generates detailed step summary |
| 229 | |
| 230 | **Artifacts Generated**: |
| 231 | |
| 232 | * `logs/markdown-link-check-results.json` - Complete validation results including: |
| 233 | * Timestamp and script name |
| 234 | * Summary statistics (total files, broken links count) |
| 235 | * List of all broken links with file paths |
| 236 | |
| 237 | **GitHub Actions Integration**: |
| 238 | |
| 239 | * Workflow: `.github/workflows/markdown-link-check.yml` |
| 240 | * Configuration: `markdown-link-check.config.json` |
| 241 | * Artifacts: `markdown-link-check-results` (JSON) |
| 242 | * Annotations: Error for each broken link |
| 243 | * Exit Code: Non-zero if broken links found |
| 244 | |
| 245 | ### Copyright Header Validation |
| 246 | |
| 247 | #### `Test-CopyrightHeaders.ps1` |
| 248 | |
| 249 | Validates copyright and SPDX license headers in source files. |
| 250 | |
| 251 | **Purpose**: Ensure all PowerShell and shell scripts include the required Microsoft copyright notice and MIT SPDX license identifier in their first 15 lines. |
| 252 | |
| 253 | **Features**: |
| 254 | |
| 255 | * Scans `.ps1`, `.psm1`, `.psd1`, and `.sh` files recursively |
| 256 | * Checks for `Copyright (c) Microsoft Corporation` header |
| 257 | * Checks for `SPDX-License-Identifier: MIT` identifier |
| 258 | * Configurable file extensions and exclude paths |
| 259 | * Exports JSON results with per-file compliance details |
| 260 | * Calculates compliance percentage across all scanned files |
| 261 | |
| 262 | **Parameters**: |
| 263 | |
| 264 | * `-Path` (string) - Root path to scan (default: repository root via `git rev-parse --show-toplevel`) |
| 265 | * `-FileExtensions` (string[]) - File extensions to check (default: `@('*.ps1', '*.psm1', '*.psd1', '*.sh')`) |
| 266 | * `-OutputPath` (string) - Path for JSON results (default: `logs/copyright-header-results.json`) |
| 267 | * `-FailOnMissing` (switch) - Exit with code 1 if any files lack required headers |
| 268 | * `-ExcludePaths` (string[]) - Directories to exclude (default: `@('node_modules', '.git', 'vendor', 'logs')`) |
| 269 | |
| 270 | **Usage**: |
| 271 | |
| 272 | ```powershell |
| 273 | # Check all source files (report only) |
| 274 | ./scripts/linting/Test-CopyrightHeaders.ps1 |
| 275 | |
| 276 | # Check and fail on missing headers |
| 277 | ./scripts/linting/Test-CopyrightHeaders.ps1 -FailOnMissing |
| 278 | |
| 279 | # Check specific path with verbose output |
| 280 | ./scripts/linting/Test-CopyrightHeaders.ps1 -Path ./scripts -FailOnMissing -Verbose |
| 281 | ``` |
| 282 | |
| 283 | **GitHub Actions Integration**: |
| 284 | |
| 285 | * Workflow: `.github/workflows/copyright-headers.yml` |
| 286 | * Artifacts: `copyright-header-results` (JSON) |
| 287 | * Exit Code: Non-zero if validation fails (with `-FailOnMissing`) |
| 288 | |
| 289 | ## Shared Module |
| 290 | |
| 291 | ### `Modules/LintingHelpers.psm1` |
| 292 | |
| 293 | Common helper functions for file discovery and git operations. |
| 294 | |
| 295 | **Exported Functions**: |
| 296 | |
| 297 | #### `Get-ChangedFilesFromGit` |
| 298 | |
| 299 | Detects files changed in current branch compared to main. |
| 300 | |
| 301 | **Parameters**: |
| 302 | |
| 303 | * `-BaseBranch` (string) - Base branch to compare against (default: `origin/main`) |
| 304 | * `-FileExtensions` (string[]) - Array of file patterns to filter (e.g., `@('*.ps1', '*.md')`) |
| 305 | |
| 306 | **Returns**: Array of changed file paths |
| 307 | |
| 308 | **Fallbacks**: |
| 309 | |
| 310 | 1. `git merge-base` with specified base branch |
| 311 | 2. `git diff HEAD~1` when merge-base fails |
| 312 | 3. `git diff HEAD` for staged/unstaged files |
| 313 | |
| 314 | #### `Get-FilesRecursive` |
| 315 | |
| 316 | Recursively finds files matching patterns with gitignore support. |
| 317 | |
| 318 | **Parameters**: |
| 319 | |
| 320 | * `-Path` (string, required) - Root directory to search from |
| 321 | * `-Include` (string[], required) - File patterns to include (e.g., `@('*.ps1', '*.psm1')`) |
| 322 | * `-GitIgnorePath` (string) - Path to `.gitignore` file for exclusion patterns |
| 323 | |
| 324 | **Returns**: Array of FileInfo objects |
| 325 | |
| 326 | **Respects**: `.gitignore` patterns when `-GitIgnorePath` is provided |
| 327 | |
| 328 | #### `Get-GitIgnorePatterns` |
| 329 | |
| 330 | Parses `.gitignore` into PowerShell wildcard patterns. |
| 331 | |
| 332 | **Parameters**: |
| 333 | |
| 334 | * `-GitIgnorePath` (string, required) - Path to `.gitignore` file |
| 335 | |
| 336 | **Returns**: Array of wildcard patterns using platform-appropriate separators |
| 337 | |
| 338 | ### `scripts/lib/Modules/CIHelpers.psm1` |
| 339 | |
| 340 | CI helper functions for annotations, outputs, environment flags, and summaries. |
| 341 | |
| 342 | **Exported Functions**: |
| 343 | |
| 344 | #### `Write-CIAnnotation` |
| 345 | |
| 346 | Creates a CI annotation. |
| 347 | |
| 348 | **Parameters**: |
| 349 | |
| 350 | * `-Level` ('Error'|'Warning'|'Notice') - Annotation severity |
| 351 | * `-Message` (string) - Annotation text |
| 352 | * `-File` (string, optional) - File path |
| 353 | * `-Line` (int, optional) - Line number |
| 354 | * `-Column` (int, optional) - Column number |
| 355 | |
| 356 | **Output**: CI annotation command |
| 357 | |
| 358 | #### `Write-CIAnnotations` |
| 359 | |
| 360 | Writes CI annotations from a validation summary. |
| 361 | |
| 362 | **Parameters**: |
| 363 | |
| 364 | * `-Summary` (ValidationSummary) - Validation results to annotate |
| 365 | |
| 366 | #### `Set-CIOutput` |
| 367 | |
| 368 | Sets a CI output variable. |
| 369 | |
| 370 | **Parameters**: |
| 371 | |
| 372 | * `-Name` (string) - Variable name |
| 373 | * `-Value` (string) - Variable value |
| 374 | |
| 375 | #### `Set-CIEnv` |
| 376 | |
| 377 | Sets a CI environment variable. |
| 378 | |
| 379 | **Parameters**: |
| 380 | |
| 381 | * `-Name` (string) - Variable name |
| 382 | * `-Value` (string) - Variable value |
| 383 | |
| 384 | #### `Write-CIStepSummary` |
| 385 | |
| 386 | Appends content to the CI step summary. |
| 387 | |
| 388 | **Parameters**: |
| 389 | |
| 390 | * `-Content` (string) - Markdown content |
| 391 | |
| 392 | **Usage Example**: |
| 393 | |
| 394 | ```powershell |
| 395 | Import-Module ./Modules/LintingHelpers.psm1 |
| 396 | |
| 397 | # Get changed PowerShell files |
| 398 | $files = Get-ChangedFilesFromGit -FileExtension '.ps1' |
| 399 | |
| 400 | # Create error annotation |
| 401 | Write-CIAnnotation -Level 'Error' -Message 'Syntax error' -File 'script.ps1' -Line 42 |
| 402 | |
| 403 | # Set output variable |
| 404 | Set-CIOutput -Name 'files-analyzed' -Value $files.Count |
| 405 | |
| 406 | # Add to step summary |
| 407 | Write-CIStepSummary -Content "## Results`n`nAnalyzed $($files.Count) files" |
| 408 | ``` |
| 409 | |
| 410 | ## Configuration Files |
| 411 | |
| 412 | ### Configuration: `PSScriptAnalyzer.psd1` |
| 413 | |
| 414 | PSScriptAnalyzer rule configuration. |
| 415 | |
| 416 | **Key Settings**: |
| 417 | |
| 418 | * Severity: Error, Warning |
| 419 | * IncludeRules: Best practices, security, performance |
| 420 | * ExcludeRules: `PSAvoidUsingWriteHost` |
| 421 | |
| 422 | ### `markdown-link-check.config.json` |
| 423 | |
| 424 | Markdown link checker configuration. |
| 425 | |
| 426 | **Key Settings**: |
| 427 | |
| 428 | * Retry attempts: 3 |
| 429 | * Timeout: 10 seconds |
| 430 | * Ignore patterns: Localhost, example.com |
| 431 | |
| 432 | ## Testing |
| 433 | |
| 434 | All scripts support local testing before running in GitHub Actions: |
| 435 | |
| 436 | ```powershell |
| 437 | # Test PSScriptAnalyzer |
| 438 | ./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -Verbose |
| 439 | |
| 440 | # Test frontmatter validation |
| 441 | ./scripts/linting/Validate-MarkdownFrontmatter.ps1 -ChangedFilesOnly |
| 442 | |
| 443 | # Test link language check |
| 444 | ./scripts/linting/Invoke-LinkLanguageCheck.ps1 |
| 445 | |
| 446 | # Test markdown links |
| 447 | ./scripts/linting/Markdown-Link-Check.ps1 |
| 448 | |
| 449 | # Test shared module |
| 450 | Import-Module ./scripts/linting/Modules/LintingHelpers.psm1 |
| 451 | Get-Command -Module LintingHelpers |
| 452 | ``` |
| 453 | |
| 454 | ## GitHub Actions Workflows |
| 455 | |
| 456 | All linting scripts are integrated into GitHub Actions workflows: |
| 457 | |
| 458 | * **PSScriptAnalyzer**: `.github/workflows/psscriptanalyzer.yml` |
| 459 | * **YAML Lint**: `.github/workflows/yaml-lint.yml` |
| 460 | * **Frontmatter Validation**: `.github/workflows/frontmatter-validation.yml` |
| 461 | * **Link Language Check**: `.github/workflows/link-lang-check.yml` |
| 462 | * **Markdown Link Check**: `.github/workflows/markdown-link-check.yml` |
| 463 | |
| 464 | See [GitHub Workflows Documentation](../../.github/workflows/README.md) for details. |
| 465 | |
| 466 | ## Adding New Linting Scripts |
| 467 | |
| 468 | To add a new linting script: |
| 469 | |
| 470 | 1. **Create wrapper script** following `Invoke-*.ps1` naming convention |
| 471 | 2. **Import LintingHelpers and CIHelpers modules** for file discovery and CI integration |
| 472 | 3. **Implement core validation logic** with clear error reporting |
| 473 | 4. **Support common parameters**: `-Verbose`, `-Debug`, `-ChangedFilesOnly` (if applicable) |
| 474 | 5. **Create GitHub Actions workflow** in `.github/workflows/` |
| 475 | 6. **Add to PR validation** in `.github/workflows/pr-validation.yml` |
| 476 | 7. **Document** in this README and workflows README |
| 477 | 8. **Test locally** before creating PR |
| 478 | |
| 479 | **Template**: |
| 480 | |
| 481 | ```powershell |
| 482 | #!/usr/bin/env pwsh |
| 483 | <# |
| 484 | .SYNOPSIS |
| 485 | Brief description of validation. |
| 486 | |
| 487 | .DESCRIPTION |
| 488 | Detailed description. |
| 489 | |
| 490 | .PARAMETER ChangedFilesOnly |
| 491 | Validate only changed files. |
| 492 | |
| 493 | .EXAMPLE |
| 494 | ./scripts/linting/Invoke-MyValidator.ps1 -Verbose |
| 495 | #> |
| 496 | |
| 497 | [CmdletBinding()] |
| 498 | param( |
| 499 | [switch]$ChangedFilesOnly |
| 500 | ) |
| 501 | |
| 502 | $ErrorActionPreference = 'Stop' |
| 503 | Import-Module (Join-Path $PSScriptRoot 'Modules/LintingHelpers.psm1') -Force |
| 504 | Import-Module (Join-Path $PSScriptRoot '../lib/Modules/CIHelpers.psm1') -Force |
| 505 | |
| 506 | #region Functions |
| 507 | |
| 508 | function Invoke-MyValidatorCore { |
| 509 | [CmdletBinding()] |
| 510 | [OutputType([void])] |
| 511 | param( |
| 512 | [switch]$ChangedFilesOnly |
| 513 | ) |
| 514 | |
| 515 | Write-Host "🔍 Running MyValidator..." |
| 516 | |
| 517 | if ($ChangedFilesOnly) { |
| 518 | $files = Get-ChangedFilesFromGit -FileExtension '.ext' |
| 519 | } |
| 520 | else { |
| 521 | $files = Get-FilesRecursive -Path (Get-Location) -Pattern '*.ext' |
| 522 | } |
| 523 | |
| 524 | if ($files.Count -eq 0) { |
| 525 | Write-Host "✅ No files to validate" |
| 526 | return |
| 527 | } |
| 528 | |
| 529 | # Perform validation |
| 530 | $issues = @() |
| 531 | foreach ($file in $files) { |
| 532 | # Validation logic here |
| 533 | if ($issue) { |
| 534 | $issues += $issue |
| 535 | Write-CIAnnotation -Level 'Error' -Message 'Issue found' -File $file |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | Write-CIStepSummary -Content "## Validation Results`n`nFound $($issues.Count) issues" |
| 540 | |
| 541 | if ($issues.Count -gt 0) { |
| 542 | throw "Found $($issues.Count) issues" |
| 543 | } |
| 544 | |
| 545 | Write-Host "✅ All files validated successfully" |
| 546 | } |
| 547 | |
| 548 | #endregion Functions |
| 549 | |
| 550 | #region Main Execution |
| 551 | if ($MyInvocation.InvocationName -ne '.') { |
| 552 | try { |
| 553 | Invoke-MyValidatorCore @PSBoundParameters |
| 554 | exit 0 |
| 555 | } |
| 556 | catch { |
| 557 | Write-Error -ErrorAction Continue "Invoke-MyValidator failed: $($_.Exception.Message)" |
| 558 | Write-CIAnnotation -Message $_.Exception.Message -Level Error |
| 559 | exit 1 |
| 560 | } |
| 561 | } |
| 562 | #endregion Main Execution |
| 563 | ``` |
| 564 | |
| 565 | ## Contributing |
| 566 | |
| 567 | When modifying linting scripts: |
| 568 | |
| 569 | 1. Follow PowerShell best practices (PSScriptAnalyzer compliant) |
| 570 | 2. Maintain CI integration patterns |
| 571 | 3. Keep scripts testable locally without GitHub Actions |
| 572 | 4. Update documentation in README files |
| 573 | 5. Test thoroughly before creating PR |
| 574 | 6. Get CODEOWNERS approval |
| 575 | |
| 576 | ## Related Documentation |
| 577 | |
| 578 | * [Scripts Documentation](../README.md) |
| 579 | * [GitHub Workflows Documentation](../../.github/workflows/README.md) |
| 580 | * [Contributing Guidelines](../../CONTRIBUTING.md) |
| 581 | |
| 582 | --- |
| 583 | |
| 584 | 🤖 Crafted with precision by ✨Copilot following brilliant human instruction, then carefully refined by our team of discerning human reviewers. |
| 585 | |