Analyze only changed files
April 23, 2026 · View on GitHub
This directory contains PowerShell scripts for validating code quality and documentation standards in the hve-core repository.
Architecture
The linting scripts follow a modular architecture with shared helper functions:
| Component | Description |
|---|---|
Wrapper Scripts (Invoke-*.ps1) | Entry points that orchestrate validation logic |
| Core Scripts | Existing validation logic (e.g., Link-Lang-Check.ps1, Validate-MarkdownFrontmatter.ps1) |
Shared Module (Modules/LintingHelpers.psm1) | Common functions for file discovery and git operations |
CI Helpers (scripts/lib/Modules/CIHelpers.psm1) | CI annotations, outputs, env flags, and step summaries |
| Configuration Files | Tool-specific settings (e.g., PSScriptAnalyzer.psd1, markdown-link-check.config.json) |
Scripts
PowerShell Linting
Invoke-PSScriptAnalyzer.ps1
Static analysis for PowerShell scripts using PSScriptAnalyzer.
Purpose: Enforce PowerShell best practices and detect common issues.
Features
- Detects changed PowerShell files via Git
- Supports analyzing all files or changed files only
- Creates CI annotations for violations
- Exports JSON results and markdown summary
- Configurable via
PSScriptAnalyzer.psd1
Parameters
-ChangedFilesOnly(switch) - Analyze only files changed in current branch
Usage
# Analyze all PowerShell files
./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -Verbose
# Analyze only changed files
./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -ChangedFilesOnly
# View detailed output
./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -Verbose -Debug
GitHub Actions Integration
- Workflow:
.github/workflows/ps-script-analyzer.yml - Artifacts:
psscriptanalyzer-results(JSON + markdown) - Exit Code: Non-zero if violations found
PSScriptAnalyzer.psd1
Configuration file for PSScriptAnalyzer rules.
Enforced Rules
| Rule Category | Description |
|---|---|
| Severity | Error and Warning levels |
| Best Practices | Avoid aliases, use approved verbs, singular nouns |
| Help | Require comment-based help |
| Security | Check for credentials in code |
| Performance | Identify inefficient patterns |
Excluded Rules
PSAvoidUsingWriteHost- Allowed for script output
YAML Linting
Invoke-YamlLint.ps1
Static analysis for GitHub Actions workflow files using actionlint.
Purpose: Validate GitHub Actions workflow YAML syntax and best practices.
Features
- Validates
.github/workflows/*.ymland.yamlfiles - Detects changed workflow files via Git
- Supports analyzing all files or changed files only
- Creates CI annotations for violations
- Exports JSON results and markdown summary
- Configurable via
.github/actionlint.yaml
Parameters
-ChangedFilesOnly(switch) - Analyze only files changed in current branch-BaseBranch(string) - Base branch for comparison (default:origin/main)-OutputPath(string) - Output path for JSON results (default:logs/yaml-lint-results.json)
Usage
# Analyze all workflow files
./scripts/linting/Invoke-YamlLint.ps1 -Verbose
# Analyze only changed files
./scripts/linting/Invoke-YamlLint.ps1 -ChangedFilesOnly
# View detailed output
./scripts/linting/Invoke-YamlLint.ps1 -Verbose -Debug
GitHub Actions Integration
- Workflow:
.github/workflows/yaml-lint.yml - Configuration:
.github/actionlint.yaml - Artifacts:
yaml-lint-results(JSON) - Exit Code: Non-zero if violations found
Markdown Validation
Validate-MarkdownFrontmatter.ps1
Validates YAML frontmatter and footer format in markdown files.
Purpose: Ensure consistent metadata across documentation.
Features
- Validates required frontmatter fields
- Checks footer format and copyright notice
- Supports changed files only mode
- Configurable warnings-as-errors
- Creates CI annotations for all issues
- Exports JSON results with detailed statistics
- Generates comprehensive step summary
Parameters
-ChangedFilesOnly(switch) - Validate only changed markdown files-SkipFooterValidation(switch) - Skip footer checks-WarningsAsErrors(switch) - Treat warnings as errors-EnableSchemaValidation(switch) - Enable JSON Schema validation (advisory only)
Artifacts Generated
logs/frontmatter-validation-results.json- Complete validation results including:- Timestamp and script name
- Summary statistics (total files, error/warning counts)
- Lists of all errors and warnings
Usage
# Validate all markdown files
./scripts/linting/Validate-MarkdownFrontmatter.ps1
# Validate only changed files
./scripts/linting/Validate-MarkdownFrontmatter.ps1 -ChangedFilesOnly
# Skip footer validation
./scripts/linting/Validate-MarkdownFrontmatter.ps1 -SkipFooterValidation
GitHub Actions Integration
- Workflow:
.github/workflows/frontmatter-validation.yml - Artifacts:
frontmatter-validation-results(JSON) - Annotations: Errors and warnings with file paths
- Exit Code: Non-zero if validation fails
Invoke-LinkLanguageCheck.ps1
Detects URLs with language paths (e.g., /en-us/) that should be removed.
Purpose: Ensure language-agnostic URLs for better internationalization.
Features
- Scans all markdown files recursively
- Calls
Link-Lang-Check.ps1for detection logic - Creates CI warning annotations
- Provides fix instructions in summary
Parameters
-ExcludePaths(string array) - Paths to exclude from the check (default:@())-OutputPath(string) - Path where the JSON result file is written; the parent directory is created automatically if it does not exist (default:logs/link-lang-check-results.json)
Usage
# Check all markdown files
./scripts/linting/Invoke-LinkLanguageCheck.ps1 -Verbose
# View detection details
./scripts/linting/Invoke-LinkLanguageCheck.ps1 -Debug
# Write results to a custom location
./scripts/linting/Invoke-LinkLanguageCheck.ps1 -OutputPath "custom/results.json"
GitHub Actions Integration
- Workflow:
.github/workflows/link-lang-check.yml - Annotations: Warnings on files with language paths
- Artifacts:
link-lang-check-results(JSON + markdown)
Link-Lang-Check.ps1
Core logic for detecting language paths in URLs.
Detection Pattern: Matches /[a-z]{2}-[a-z]{2}/ patterns in Microsoft domain URLs.
Markdown-Link-Check.ps1
Validates all links in markdown files using markdown-link-check npm package.
Purpose: Detect broken links before deployment.
Features
- Checks internal and external links
- Configurable via
markdown-link-check.config.json - Retries failed links
- Respects robots.txt
- Creates CI annotations for broken links
- Exports JSON results with link statistics
- Generates detailed step summary
Artifacts Generated
logs/markdown-link-check-results.json- Complete validation results including:- Timestamp and script name
- Summary statistics (total files, broken links count)
- List of all broken links with file paths
GitHub Actions Integration
- Workflow:
.github/workflows/markdown-link-check.yml - Configuration:
markdown-link-check.config.json - Artifacts:
markdown-link-check-results(JSON) - Annotations: Error for each broken link
- Exit Code: Non-zero if broken links found
Skill Structure Validation
Validate-SkillStructure.ps1
Validates the structural integrity of skill directories under .github/skills/.
Purpose: Ensure all skill packages comply with the agentskills.io specification and hve-core conventions.
Features
- Validates SKILL.md presence in each skill directory
- Checks frontmatter for required
nameanddescriptionfields - Verifies
namematches directory name - When
scripts/subdirectory exists, requires both.ps1and.shfiles for cross-platform support - Validates Python skills with
tests/includetests/fuzz_harness.pyfor Scorecard compliance - Warns on unrecognized directories
- Supports changed-files-only mode via Git
- Creates CI annotations for violations
- Exports JSON results to
logs/skill-validation-results.json
Parameters
-SkillsPath(string) - Root path containing skill directories (default:.github/skills)-WarningsAsErrors(switch) - Treat warnings as errors-ChangedFilesOnly(switch) - Validate only skills with changed files-BaseBranch(string) - Git reference for changed file detection (default:origin/main)
Usage
# Validate all skills
./scripts/linting/Validate-SkillStructure.ps1
# Validate with warnings as errors
./scripts/linting/Validate-SkillStructure.ps1 -WarningsAsErrors
# Validate only changed skills
./scripts/linting/Validate-SkillStructure.ps1 -ChangedFilesOnly
GitHub Actions Integration
- Workflow:
.github/workflows/skill-validation.yml - Artifacts:
skill-validation-results(JSON) - Exit Code: Non-zero if validation fails
Copyright Header Validation
Test-CopyrightHeaders.ps1
Validates copyright and SPDX license headers in source files.
Purpose: Ensure all PowerShell, shell, and Python scripts include the required Microsoft copyright notice and MIT SPDX license identifier in their first 15 lines.
Features
- Scans
.ps1,.psm1,.psd1,.sh, and.pyfiles recursively - Checks for
Copyright (c) Microsoft Corporationheader - Checks for
SPDX-License-Identifier: MITidentifier - Configurable file extensions and exclude paths
- Exports JSON results with per-file compliance details
- Calculates compliance percentage across all scanned files
Parameters
-Path(string) - Root path to scan (default: repository root viagit rev-parse --show-toplevel)-FileExtensions(string[]) - File extensions to check (default:@('*.ps1', '*.psm1', '*.psd1', '*.sh', '*.py'))-OutputPath(string) - Path for JSON results (default:logs/copyright-header-results.json)-FailOnMissing(switch) - Exit with code 1 if any files lack required headers-ExcludePaths(string[]) - Directories to exclude (default:@('node_modules', '.git', 'vendor', 'logs'))
Usage
# Check all source files (report only)
./scripts/linting/Test-CopyrightHeaders.ps1
# Check and fail on missing headers
./scripts/linting/Test-CopyrightHeaders.ps1 -FailOnMissing
# Check specific path with verbose output
./scripts/linting/Test-CopyrightHeaders.ps1 -Path ./scripts -FailOnMissing -Verbose
GitHub Actions Integration
- Workflow:
.github/workflows/copyright-headers.yml - Artifacts:
copyright-header-results(JSON) - Exit Code: Non-zero if validation fails (with
-FailOnMissing)
ms.date Freshness Check
Invoke-MsDateFreshnessCheck.ps1
Checks ms.date frontmatter freshness across markdown files.
Purpose: Flag documentation files whose ms.date exceeds a configurable staleness threshold, helping teams identify content that may need review or updates.
Features
- Scans all markdown files or only changed files via Git
- Configurable staleness threshold in days
- Generates JSON report and markdown step summary
- Creates CI annotations for each stale file
- Excludes
CHANGELOG.mdand.copilot-tracking/by default
Parameters
-ThresholdDays(int) - Days before ms.date is considered stale (default:90)-Paths(string[]) - Directories to scan (default: repository root)-ChangedFilesOnly(switch) - Only check files changed relative to BaseBranch-BaseBranch(string) - Base branch for changed-file detection (default:origin/main)
Artifacts Generated
logs/msdate-freshness-results.json- Structured results with stale file detailslogs/msdate-summary.md- Markdown step summary with stale files table
Usage
# Check all markdown files with default 90-day threshold
./scripts/linting/Invoke-MsDateFreshnessCheck.ps1
# Use a stricter 60-day threshold
./scripts/linting/Invoke-MsDateFreshnessCheck.ps1 -ThresholdDays 60
# Check only changed files
./scripts/linting/Invoke-MsDateFreshnessCheck.ps1 -ChangedFilesOnly
GitHub Actions Integration
- Workflow:
.github/workflows/msdate-freshness-check.yml - Artifacts:
msdate-freshness-results(JSON + markdown) - npm script:
npm run lint:frontmatter(frontmatter check also includes freshness)
Python Tooling
Invoke-PythonLint.ps1
Lints Python skills using ruff.
Purpose: Enforce Python code quality standards across all Python skills in the repository by dynamically discovering and linting each skill.
Features
- Discovers Python skills via
pyproject.tomlfile search - Verifies ruff availability before running
- Lints each skill directory independently
- Reports per-skill pass/fail results
- Supports optional JSON output
Parameters
-RepoRoot(string) - Repository root path (default: current directory)-OutputPath(string) - Optional path for JSON results
Usage
# Lint all Python skills
./scripts/linting/Invoke-PythonLint.ps1
# Lint from a specific repository root
./scripts/linting/Invoke-PythonLint.ps1 -RepoRoot /path/to/repo
GitHub Actions Integration
- Workflow:
.github/workflows/python-lint.yml - npm script:
npm run lint:py
Invoke-PythonTests.ps1
Runs pytest across Python skills.
Purpose: Execute Python test suites for all Python skills that include a tests/ directory, reporting aggregate pass/fail results.
Features
- Discovers Python skills via
pyproject.tomlfile search - Skips skills without a
tests/directory - Verifies pytest availability before running
- Reports per-skill test results with pass/fail counts
- Configurable verbosity level
Parameters
-RepoRoot(string) - Repository root path (default: current directory)-OutputPath(string) - Optional path for JSON results-Verbosity(string) - pytest verbosity flag (default:-v)
Usage
# Run all Python tests
./scripts/linting/Invoke-PythonTests.ps1
# Run with quiet output
./scripts/linting/Invoke-PythonTests.ps1 -Verbosity '-q'
GitHub Actions Integration
- Workflow:
.github/workflows/pytest-tests.yml - npm script:
npm run test:py
Shared Module
Modules/FrontmatterValidation.psm1
Frontmatter validation functions and types used by Validate-MarkdownFrontmatter.ps1 and its test suite.
Imported via using module syntax in test files.
Exported Classes
| Class | Purpose |
|---|---|
ValidationIssue | Represents a single validation finding with type, field, message, and file |
FileTypeInfo | Classifies a markdown file by type (docs, prompt, instruction, agent, etc.) |
FileValidationResult | Aggregates all validation issues and frontmatter for a single file |
Usage
using module ./scripts/linting/Modules/FrontmatterValidation.psm1
$issue = [ValidationIssue]::new('Error', 'ms.date', 'Missing required field', 'README.md')
Modules/LintingHelpers.psm1
Common helper functions for file discovery and git operations.
Exported Functions
Get-ChangedFilesFromGit
Detects files changed in current branch compared to main.
Parameters
-BaseBranch(string) - Base branch to compare against (default:origin/main)-FileExtensions(string[]) - Array of file patterns to filter (e.g.,@('*.ps1', '*.md'))
Returns: Array of changed file paths
Fallbacks
git merge-basewith specified base branchgit diff HEAD~1when merge-base failsgit diff HEADfor staged/unstaged files
Get-FilesRecursive
Finds files matching patterns using git ls-files with a Get-ChildItem fallback.
Parameters
-Path(string, required) - Root directory to search from-Include(string[], required) - File patterns to include (e.g.,@('*.ps1', '*.psm1'))-GitIgnorePath(string) - Path to.gitignorefile for exclusion patterns (fallback path only)
Returns: Array of FileInfo objects
Behavior
- Inside a git repository, uses
git ls-files --cached --others --exclude-standardscoped to the given path. Git natively handles.gitignoreexclusions. - Outside a git repository (or when
gitis unavailable), falls back toGet-ChildItem -Recursewith optional-GitIgnorePathfiltering.
Get-GitIgnorePatterns
Parses .gitignore into PowerShell wildcard patterns.
Parameters
-GitIgnorePath(string, required) - Path to.gitignorefile
Returns: Array of wildcard patterns using platform-appropriate separators
scripts/lib/Modules/CIHelpers.psm1
CI helper functions for annotations, outputs, environment flags, and summaries.
Exported Functions
Write-CIAnnotation
Creates a CI annotation.
Parameters
-Level('Error'|'Warning'|'Notice') - Annotation severity-Message(string) - Annotation text-File(string, optional) - File path-Line(int, optional) - Line number-Column(int, optional) - Column number
Output: CI annotation command
Write-CIAnnotations
Writes CI annotations from a validation summary.
Parameters
-Summary(ValidationSummary) - Validation results to annotate
Set-CIOutput
Sets a CI output variable.
Parameters
-Name(string) - Variable name-Value(string) - Variable value
Set-CIEnv
Sets a CI environment variable.
Parameters
-Name(string) - Variable name-Value(string) - Variable value
Write-CIStepSummary
Appends content to the CI step summary.
Parameters
-Content(string) - Markdown content
Usage Example
Import-Module ./Modules/LintingHelpers.psm1
# Get changed PowerShell files
$files = Get-ChangedFilesFromGit -FileExtension '.ps1'
# Create error annotation
Write-CIAnnotation -Level 'Error' -Message 'Syntax error' -File 'script.ps1' -Line 42
# Set output variable
Set-CIOutput -Name 'files-analyzed' -Value $files.Count
# Add to step summary
Write-CIStepSummary -Content "## Results`n`nAnalyzed $($files.Count) files"
Configuration Files
Configuration: PSScriptAnalyzer.psd1
PSScriptAnalyzer rule configuration.
Key Settings
- Severity: Error, Warning
- IncludeRules: Best practices, security, performance
- ExcludeRules:
PSAvoidUsingWriteHost
markdown-link-check.config.json
Markdown link checker configuration.
Key Settings
- Retry attempts: 3
- Timeout: 10 seconds
- Ignore patterns: Localhost, example.com
Testing
All scripts support local testing before running in GitHub Actions:
# Test PSScriptAnalyzer
./scripts/linting/Invoke-PSScriptAnalyzer.ps1 -Verbose
# Test frontmatter validation
./scripts/linting/Validate-MarkdownFrontmatter.ps1 -ChangedFilesOnly
# Test link language check
./scripts/linting/Invoke-LinkLanguageCheck.ps1
# Test markdown links
./scripts/linting/Markdown-Link-Check.ps1
# Test shared module
Import-Module ./scripts/linting/Modules/LintingHelpers.psm1
Get-Command -Module LintingHelpers
GitHub Actions Workflows
All linting scripts are integrated into GitHub Actions workflows:
| Script | Workflow |
|---|---|
| PSScriptAnalyzer | .github/workflows/ps-script-analyzer.yml |
| YAML Lint | .github/workflows/yaml-lint.yml |
| Frontmatter Validation | .github/workflows/frontmatter-validation.yml |
| Link Language Check | .github/workflows/link-lang-check.yml |
| Markdown Link Check | .github/workflows/markdown-link-check.yml |
| ms.date Freshness | .github/workflows/msdate-freshness-check.yml |
| Python Lint | .github/workflows/python-lint.yml |
| Python Tests | .github/workflows/pytest-tests.yml |
| Copyright Headers | .github/workflows/copyright-headers.yml |
| Skill Validation | .github/workflows/skill-validation.yml |
See GitHub Workflows Documentation for details.
Adding New Linting Scripts
To add a new linting script:
- Create wrapper script following
Invoke-*.ps1naming convention - Import LintingHelpers and CIHelpers modules for file discovery and CI integration
- Implement core validation logic with clear error reporting
- Support common parameters:
-Verbose,-Debug,-ChangedFilesOnly(if applicable) - Create GitHub Actions workflow in
.github/workflows/ - Add to PR validation in
.github/workflows/pr-validation.yml - Document in this README and workflows README
- Test locally before creating PR
Template
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Brief description of validation.
.DESCRIPTION
Detailed description.
.PARAMETER ChangedFilesOnly
Validate only changed files.
.EXAMPLE
./scripts/linting/Invoke-MyValidator.ps1 -Verbose
#>
[CmdletBinding()]
param(
[switch]$ChangedFilesOnly
)
$ErrorActionPreference = 'Stop'
Import-Module (Join-Path $PSScriptRoot 'Modules/LintingHelpers.psm1') -Force
Import-Module (Join-Path $PSScriptRoot '../lib/Modules/CIHelpers.psm1') -Force
#region Functions
function Invoke-MyValidatorCore {
[CmdletBinding()]
[OutputType([void])]
param(
[switch]$ChangedFilesOnly
)
Write-Host "🔍 Running MyValidator..."
if ($ChangedFilesOnly) {
$files = Get-ChangedFilesFromGit -FileExtension '.ext'
}
else {
$files = Get-FilesRecursive -Path (Get-Location) -Pattern '*.ext'
}
if ($files.Count -eq 0) {
Write-Host "✅ No files to validate"
return
}
# Perform validation
$issues = @()
foreach ($file in $files) {
# Validation logic here
if ($issue) {
$issues += $issue
Write-CIAnnotation -Level 'Error' -Message 'Issue found' -File $file
}
}
Write-CIStepSummary -Content "## Validation Results`n`nFound $($issues.Count) issues"
if ($issues.Count -gt 0) {
throw "Found $($issues.Count) issues"
}
Write-Host "✅ All files validated successfully"
}
#endregion Functions
#region Main Execution
if ($MyInvocation.InvocationName -ne '.') {
try {
Invoke-MyValidatorCore @PSBoundParameters
exit 0
}
catch {
Write-Error -ErrorAction Continue "Invoke-MyValidator failed: $($_.Exception.Message)"
Write-CIAnnotation -Message $_.Exception.Message -Level Error
exit 1
}
}
#endregion Main Execution
Contributing
When modifying linting scripts:
- Follow PowerShell best practices (PSScriptAnalyzer compliant)
- Maintain CI integration patterns
- Keep scripts testable locally without GitHub Actions
- Update documentation in README files
- Test thoroughly before creating PR
- Get CODEOWNERS approval
Related Documentation
🤖 Crafted with precision by ✨Copilot following brilliant human instruction, then carefully refined by our team of discerning human reviewers.