microsoft/hve-core
Publicmirrored fromhttps://github.com/microsoft/hve-coreAvailable
scripts/linting/README.md
498lines · 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 GitHub Actions integration |
| 25 | * **Configuration Files** - Tool-specific settings (e.g., `PSScriptAnalyzer.psd1`, `markdown-link-check.config.json`) |
| 26 | |
| 27 | ## Scripts |
| 28 | |
| 29 | ### PowerShell Linting |
| 30 | |
| 31 | #### `Invoke-PSScriptAnalyzer.ps1` |
| 32 | |
| 33 | Static analysis for PowerShell scripts using PSScriptAnalyzer. |
| 34 | |
| 35 | **Purpose**: Enforce PowerShell best practices and detect common issues. |
| 36 | |
| 37 | **Features**: |
| 38 | |
| 39 | * Detects changed PowerShell files via Git |
| 40 | * Supports analyzing all files or changed files only |
| 41 | * Creates GitHub Actions annotations for violations |
| 42 | * Exports JSON results and markdown summary |
| 43 | * Configurable via `PSScriptAnalyzer.psd1` |
| 44 | |
| 45 | **Parameters**: |
| 46 | |
| 47 | * `-ChangedFilesOnly` (switch) - Analyze only files changed in current branch |
| 48 | |
| 49 | **Usage**: |
| 50 | |
| 51 | ```powershell |
| 52 | # Analyze all PowerShell files |
| 53 | ./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -Verbose |
| 54 | |
| 55 | # Analyze only changed files |
| 56 | ./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -ChangedFilesOnly |
| 57 | |
| 58 | # View detailed output |
| 59 | ./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -Verbose -Debug |
| 60 | ``` |
| 61 | |
| 62 | **GitHub Actions Integration**: |
| 63 | |
| 64 | * Workflow: `.github/workflows/psscriptanalyzer.yml` |
| 65 | * Artifacts: `psscriptanalyzer-results` (JSON + markdown) |
| 66 | * Exit Code: Non-zero if violations found |
| 67 | |
| 68 | #### `PSScriptAnalyzer.psd1` |
| 69 | |
| 70 | Configuration file for PSScriptAnalyzer rules. |
| 71 | |
| 72 | **Enforced Rules**: |
| 73 | |
| 74 | * **Severity**: Error and Warning levels |
| 75 | * **Best Practices**: Avoid aliases, use approved verbs, singular nouns |
| 76 | * **Help**: Require comment-based help |
| 77 | * **Security**: Check for credentials in code |
| 78 | * **Performance**: Identify inefficient patterns |
| 79 | |
| 80 | **Excluded Rules**: |
| 81 | |
| 82 | * `PSAvoidUsingWriteHost` - Allowed for script output |
| 83 | |
| 84 | ### YAML Linting |
| 85 | |
| 86 | #### `Invoke-YamlLint.ps1` |
| 87 | |
| 88 | Static analysis for GitHub Actions workflow files using actionlint. |
| 89 | |
| 90 | **Purpose**: Validate GitHub Actions workflow YAML syntax and best practices. |
| 91 | |
| 92 | **Features**: |
| 93 | |
| 94 | * Validates `.github/workflows/*.yml` and `.yaml` files |
| 95 | * Detects changed workflow files via Git |
| 96 | * Supports analyzing all files or changed files only |
| 97 | * Creates GitHub Actions annotations for violations |
| 98 | * Exports JSON results and markdown summary |
| 99 | * Configurable via `.github/actionlint.yaml` |
| 100 | |
| 101 | **Parameters**: |
| 102 | |
| 103 | * `-ChangedFilesOnly` (switch) - Analyze only files changed in current branch |
| 104 | * `-BaseBranch` (string) - Base branch for comparison (default: `origin/main`) |
| 105 | * `-OutputPath` (string) - Output path for JSON results (default: `logs/yaml-lint-results.json`) |
| 106 | |
| 107 | **Usage**: |
| 108 | |
| 109 | ```powershell |
| 110 | # Analyze all workflow files |
| 111 | ./scripts/linting/Invoke-YamlLint.ps1 -Verbose |
| 112 | |
| 113 | # Analyze only changed files |
| 114 | ./scripts/linting/Invoke-YamlLint.ps1 -ChangedFilesOnly |
| 115 | |
| 116 | # View detailed output |
| 117 | ./scripts/linting/Invoke-YamlLint.ps1 -Verbose -Debug |
| 118 | ``` |
| 119 | |
| 120 | **GitHub Actions Integration**: |
| 121 | |
| 122 | * Workflow: `.github/workflows/yaml-lint.yml` |
| 123 | * Configuration: `.github/actionlint.yaml` |
| 124 | * Artifacts: `yaml-lint-results` (JSON) |
| 125 | * Exit Code: Non-zero if violations found |
| 126 | |
| 127 | ### Markdown Validation |
| 128 | |
| 129 | #### `Validate-MarkdownFrontmatter.ps1` |
| 130 | |
| 131 | Validates YAML frontmatter and footer format in markdown files. |
| 132 | |
| 133 | **Purpose**: Ensure consistent metadata across documentation. |
| 134 | |
| 135 | **Features**: |
| 136 | |
| 137 | * Validates required frontmatter fields |
| 138 | * Checks footer format and copyright notice |
| 139 | * Supports changed files only mode |
| 140 | * Configurable warnings-as-errors |
| 141 | * Creates GitHub Actions annotations for all issues |
| 142 | * Exports JSON results with detailed statistics |
| 143 | * Generates comprehensive step summary |
| 144 | |
| 145 | **Parameters**: |
| 146 | |
| 147 | * `-ChangedFilesOnly` (switch) - Validate only changed markdown files |
| 148 | * `-SkipFooterValidation` (switch) - Skip footer checks |
| 149 | * `-WarningsAsErrors` (switch) - Treat warnings as errors |
| 150 | |
| 151 | **Artifacts Generated**: |
| 152 | |
| 153 | * `logs/frontmatter-validation-results.json` - Complete validation results including: |
| 154 | * Timestamp and script name |
| 155 | * Summary statistics (total files, error/warning counts) |
| 156 | * Lists of all errors and warnings |
| 157 | |
| 158 | **Usage**: |
| 159 | |
| 160 | ```powershell |
| 161 | # Validate all markdown files |
| 162 | ./scripts/linting/Validate-MarkdownFrontmatter.ps1 |
| 163 | |
| 164 | # Validate only changed files |
| 165 | ./scripts/linting/Validate-MarkdownFrontmatter.ps1 -ChangedFilesOnly |
| 166 | |
| 167 | # Skip footer validation |
| 168 | ./scripts/linting/Validate-MarkdownFrontmatter.ps1 -SkipFooterValidation |
| 169 | ``` |
| 170 | |
| 171 | **GitHub Actions Integration**: |
| 172 | |
| 173 | * Workflow: `.github/workflows/frontmatter-validation.yml` |
| 174 | * Artifacts: `frontmatter-validation-results` (JSON) |
| 175 | * Annotations: Errors and warnings with file paths |
| 176 | * Exit Code: Non-zero if validation fails |
| 177 | |
| 178 | #### `Invoke-LinkLanguageCheck.ps1` |
| 179 | |
| 180 | Detects URLs with language paths (e.g., `/en-us/`) that should be removed. |
| 181 | |
| 182 | **Purpose**: Ensure language-agnostic URLs for better internationalization. |
| 183 | |
| 184 | **Features**: |
| 185 | |
| 186 | * Scans all markdown files recursively |
| 187 | * Calls `Link-Lang-Check.ps1` for detection logic |
| 188 | * Creates GitHub Actions warning annotations |
| 189 | * Provides fix instructions in summary |
| 190 | |
| 191 | **Usage**: |
| 192 | |
| 193 | ```powershell |
| 194 | # Check all markdown files |
| 195 | ./scripts/linting/Invoke-LinkLanguageCheck.ps1 -Verbose |
| 196 | |
| 197 | # View detection details |
| 198 | ./scripts/linting/Invoke-LinkLanguageCheck.ps1 -Debug |
| 199 | ``` |
| 200 | |
| 201 | **GitHub Actions Integration**: |
| 202 | |
| 203 | * Workflow: `.github/workflows/link-lang-check.yml` |
| 204 | * Annotations: Warnings on files with language paths |
| 205 | * Artifacts: `link-lang-check-results` (JSON + markdown) |
| 206 | |
| 207 | #### `Link-Lang-Check.ps1` |
| 208 | |
| 209 | Core logic for detecting language paths in URLs. |
| 210 | |
| 211 | **Detection Pattern**: Matches `/[a-z]{2}-[a-z]{2}/` patterns in Microsoft domain URLs. |
| 212 | |
| 213 | #### `Markdown-Link-Check.ps1` |
| 214 | |
| 215 | Validates all links in markdown files using markdown-link-check npm package. |
| 216 | |
| 217 | **Purpose**: Detect broken links before deployment. |
| 218 | |
| 219 | **Features**: |
| 220 | |
| 221 | * Checks internal and external links |
| 222 | * Configurable via `markdown-link-check.config.json` |
| 223 | * Retries failed links |
| 224 | * Respects robots.txt |
| 225 | * Creates GitHub Actions annotations for broken links |
| 226 | * Exports JSON results with link statistics |
| 227 | * Generates detailed step summary |
| 228 | |
| 229 | **Artifacts Generated**: |
| 230 | |
| 231 | * `logs/markdown-link-check-results.json` - Complete validation results including: |
| 232 | * Timestamp and script name |
| 233 | * Summary statistics (total files, broken links count) |
| 234 | * List of all broken links with file paths |
| 235 | |
| 236 | **GitHub Actions Integration**: |
| 237 | |
| 238 | * Workflow: `.github/workflows/markdown-link-check.yml` |
| 239 | * Configuration: `markdown-link-check.config.json` |
| 240 | * Artifacts: `markdown-link-check-results` (JSON) |
| 241 | * Annotations: Error for each broken link |
| 242 | * Exit Code: Non-zero if broken links found |
| 243 | |
| 244 | ## Shared Module |
| 245 | |
| 246 | ### `Modules/LintingHelpers.psm1` |
| 247 | |
| 248 | Common helper functions for GitHub Actions integration and file operations. |
| 249 | |
| 250 | **Exported Functions**: |
| 251 | |
| 252 | #### `Get-ChangedFilesFromGit` |
| 253 | |
| 254 | Detects files changed in current branch compared to main. |
| 255 | |
| 256 | **Parameters**: |
| 257 | |
| 258 | * `-FileExtension` (string) - Filter by extension (e.g., '.ps1') |
| 259 | |
| 260 | **Returns**: Array of changed file paths |
| 261 | |
| 262 | **Fallbacks**: |
| 263 | |
| 264 | 1. `git diff` vs `origin/main` |
| 265 | 2. `git diff` vs `main` |
| 266 | 3. `git ls-files` (all tracked files) |
| 267 | |
| 268 | #### `Get-FilesRecursive` |
| 269 | |
| 270 | Recursively finds files matching pattern with gitignore support. |
| 271 | |
| 272 | **Parameters**: |
| 273 | |
| 274 | * `-Path` (string) - Root directory |
| 275 | * `-Pattern` (string) - File pattern (e.g., '*.ps1') |
| 276 | |
| 277 | **Returns**: Array of matching file paths |
| 278 | |
| 279 | **Respects**: `.gitignore` patterns |
| 280 | |
| 281 | #### `Get-GitIgnorePatterns` |
| 282 | |
| 283 | Loads and parses `.gitignore` file. |
| 284 | |
| 285 | **Returns**: Array of ignore patterns |
| 286 | |
| 287 | #### `Write-GitHubAnnotation` |
| 288 | |
| 289 | Creates GitHub Actions annotation. |
| 290 | |
| 291 | **Parameters**: |
| 292 | |
| 293 | * `-Type` ('error'|'warning'|'notice') - Annotation severity |
| 294 | * `-Message` (string) - Annotation text |
| 295 | * `-File` (string, optional) - File path |
| 296 | * `-Line` (int, optional) - Line number |
| 297 | * `-Column` (int, optional) - Column number |
| 298 | |
| 299 | **Output**: GitHub Actions annotation command |
| 300 | |
| 301 | #### `Set-GitHubOutput` |
| 302 | |
| 303 | Sets GitHub Actions output variable. |
| 304 | |
| 305 | **Parameters**: |
| 306 | |
| 307 | * `-Name` (string) - Variable name |
| 308 | * `-Value` (string) - Variable value |
| 309 | |
| 310 | #### `Set-GitHubEnv` |
| 311 | |
| 312 | Sets GitHub Actions environment variable. |
| 313 | |
| 314 | **Parameters**: |
| 315 | |
| 316 | * `-Name` (string) - Variable name |
| 317 | * `-Value` (string) - Variable value |
| 318 | |
| 319 | #### `Write-GitHubStepSummary` |
| 320 | |
| 321 | Appends content to GitHub Actions step summary. |
| 322 | |
| 323 | **Parameters**: |
| 324 | |
| 325 | * `-Content` (string) - Markdown content |
| 326 | |
| 327 | **Usage Example**: |
| 328 | |
| 329 | ```powershell |
| 330 | Import-Module ./Modules/LintingHelpers.psm1 |
| 331 | |
| 332 | # Get changed PowerShell files |
| 333 | $files = Get-ChangedFilesFromGit -FileExtension '.ps1' |
| 334 | |
| 335 | # Create error annotation |
| 336 | Write-GitHubAnnotation -Type 'error' -Message 'Syntax error' -File 'script.ps1' -Line 42 |
| 337 | |
| 338 | # Set output variable |
| 339 | Set-GitHubOutput -Name 'files-analyzed' -Value $files.Count |
| 340 | |
| 341 | # Add to step summary |
| 342 | Write-GitHubStepSummary -Content "## Results`n`nAnalyzed $($files.Count) files" |
| 343 | ``` |
| 344 | |
| 345 | ## Configuration Files |
| 346 | |
| 347 | ### Configuration: `PSScriptAnalyzer.psd1` |
| 348 | |
| 349 | PSScriptAnalyzer rule configuration. |
| 350 | |
| 351 | **Key Settings**: |
| 352 | |
| 353 | * Severity: Error, Warning |
| 354 | * IncludeRules: Best practices, security, performance |
| 355 | * ExcludeRules: `PSAvoidUsingWriteHost` |
| 356 | |
| 357 | ### `markdown-link-check.config.json` |
| 358 | |
| 359 | Markdown link checker configuration. |
| 360 | |
| 361 | **Key Settings**: |
| 362 | |
| 363 | * Retry attempts: 3 |
| 364 | * Timeout: 10 seconds |
| 365 | * Ignore patterns: Localhost, example.com |
| 366 | |
| 367 | ## Testing |
| 368 | |
| 369 | All scripts support local testing before running in GitHub Actions: |
| 370 | |
| 371 | ```powershell |
| 372 | # Test PSScriptAnalyzer |
| 373 | ./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -Verbose |
| 374 | |
| 375 | # Test frontmatter validation |
| 376 | ./scripts/linting/Validate-MarkdownFrontmatter.ps1 -ChangedFilesOnly |
| 377 | |
| 378 | # Test link language check |
| 379 | ./scripts/linting/Invoke-LinkLanguageCheck.ps1 |
| 380 | |
| 381 | # Test markdown links |
| 382 | ./scripts/linting/Markdown-Link-Check.ps1 |
| 383 | |
| 384 | # Test shared module |
| 385 | Import-Module ./scripts/linting/Modules/LintingHelpers.psm1 |
| 386 | Get-Command -Module LintingHelpers |
| 387 | ``` |
| 388 | |
| 389 | ## GitHub Actions Workflows |
| 390 | |
| 391 | All linting scripts are integrated into GitHub Actions workflows: |
| 392 | |
| 393 | * **PSScriptAnalyzer**: `.github/workflows/psscriptanalyzer.yml` |
| 394 | * **YAML Lint**: `.github/workflows/yaml-lint.yml` |
| 395 | * **Frontmatter Validation**: `.github/workflows/frontmatter-validation.yml` |
| 396 | * **Link Language Check**: `.github/workflows/link-lang-check.yml` |
| 397 | * **Markdown Link Check**: `.github/workflows/markdown-link-check.yml` |
| 398 | |
| 399 | See [GitHub Workflows Documentation](../../.github/workflows/README.md) for details. |
| 400 | |
| 401 | ## Adding New Linting Scripts |
| 402 | |
| 403 | To add a new linting script: |
| 404 | |
| 405 | 1. **Create wrapper script** following `Invoke-*.ps1` naming convention |
| 406 | 2. **Import LintingHelpers module** for GitHub Actions integration |
| 407 | 3. **Implement core validation logic** with clear error reporting |
| 408 | 4. **Support common parameters**: `-Verbose`, `-Debug`, `-ChangedFilesOnly` (if applicable) |
| 409 | 5. **Create GitHub Actions workflow** in `.github/workflows/` |
| 410 | 6. **Add to PR validation** in `.github/workflows/pr-validation.yml` |
| 411 | 7. **Document** in this README and workflows README |
| 412 | 8. **Test locally** before creating PR |
| 413 | |
| 414 | **Template**: |
| 415 | |
| 416 | ```powershell |
| 417 | #!/usr/bin/env pwsh |
| 418 | <# |
| 419 | .SYNOPSIS |
| 420 | Brief description of validation. |
| 421 | |
| 422 | .DESCRIPTION |
| 423 | Detailed description. |
| 424 | |
| 425 | .PARAMETER ChangedFilesOnly |
| 426 | Validate only changed files. |
| 427 | |
| 428 | .EXAMPLE |
| 429 | ./scripts/linting/Invoke-MyValidator.ps1 -Verbose |
| 430 | #> |
| 431 | |
| 432 | [CmdletBinding()] |
| 433 | param( |
| 434 | [switch]$ChangedFilesOnly |
| 435 | ) |
| 436 | |
| 437 | # Import shared helpers |
| 438 | $scriptPath = $PSScriptRoot |
| 439 | Import-Module "$scriptPath/Modules/LintingHelpers.psm1" -Force |
| 440 | |
| 441 | # Main validation logic |
| 442 | Write-Host "🔍 Running MyValidator..." |
| 443 | |
| 444 | if ($ChangedFilesOnly) { |
| 445 | $files = Get-ChangedFilesFromGit -FileExtension '.ext' |
| 446 | } else { |
| 447 | $files = Get-FilesRecursive -Path (Get-Location) -Pattern '*.ext' |
| 448 | } |
| 449 | |
| 450 | if ($files.Count -eq 0) { |
| 451 | Write-Host "✅ No files to validate" |
| 452 | exit 0 |
| 453 | } |
| 454 | |
| 455 | # Perform validation |
| 456 | $issues = @() |
| 457 | foreach ($file in $files) { |
| 458 | # Validation logic here |
| 459 | if ($issue) { |
| 460 | $issues += $issue |
| 461 | Write-GitHubAnnotation -Type 'error' -Message 'Issue found' -File $file |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | # Export results |
| 466 | if ($env:GITHUB_ACTIONS) { |
| 467 | Write-GitHubStepSummary -Content "## Validation Results`n`nFound $($issues.Count) issues" |
| 468 | } |
| 469 | |
| 470 | if ($issues.Count -gt 0) { |
| 471 | Write-Host "❌ Found $($issues.Count) issues" |
| 472 | exit 1 |
| 473 | } |
| 474 | |
| 475 | Write-Host "✅ All files validated successfully" |
| 476 | exit 0 |
| 477 | ``` |
| 478 | |
| 479 | ## Contributing |
| 480 | |
| 481 | When modifying linting scripts: |
| 482 | |
| 483 | 1. Follow PowerShell best practices (PSScriptAnalyzer compliant) |
| 484 | 2. Maintain GitHub Actions integration patterns |
| 485 | 3. Keep scripts testable locally without GitHub Actions |
| 486 | 4. Update documentation in README files |
| 487 | 5. Test thoroughly before creating PR |
| 488 | 6. Get CODEOWNERS approval |
| 489 | |
| 490 | ## Related Documentation |
| 491 | |
| 492 | * [Scripts Documentation](../README.md) |
| 493 | * [GitHub Workflows Documentation](../../.github/workflows/README.md) |
| 494 | * [Contributing Guidelines](../../CONTRIBUTING.md) |
| 495 | |
| 496 | --- |
| 497 | |
| 498 | 🤖 Crafted with precision by ✨Copilot following brilliant human instruction, then carefully refined by our team of discerning human reviewers. |
| 499 | |