Private/Helpers.ps1
|
# Private helper functions for CodeCompass.Repo #region Standards $script:Standards = @{ core = @{ files_required = @('README.md', '.gitignore', 'LICENSE', 'CHANGELOG.md', '.editorconfig', 'CODEOWNERS') files_recommended = @('CONTRIBUTING.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/ISSUE_TEMPLATE/') structure_source = 'src' structure_tests = 'tests' structure_docs = 'docs' require_ci = $true require_test_step = $true require_lint_step = $true readme_sections = @('Overview', 'Installation', 'Usage') changelog_format = 'keep-a-changelog' require_lockfile = $true require_dep_automation = $true security_forbidden = @('*.pem', '*.key', '*.pfx', '*secret*', '*credentials*') } active = @{ files_required = @('README.md', '.gitignore', 'LICENSE') files_recommended = @() structure_source = 'src' structure_tests = 'tests' structure_docs = $null require_ci = $true require_test_step = $false require_lint_step = $false readme_sections = @('Overview', 'Installation', 'Usage') changelog_format = $null require_lockfile = $true require_dep_automation = $false security_forbidden = @('*.pem', '*.key', '*.pfx', '*secret*', '*credentials*') } minimal = @{ files_required = @('README.md', '.gitignore') files_recommended = @() structure_source = $null structure_tests = $null structure_docs = $null require_ci = $false require_test_step = $false require_lint_step = $false readme_sections = @() changelog_format = $null require_lockfile = $false require_dep_automation = $false security_forbidden = @('*.pem', '*.key', '*.pfx') } } #endregion #region Check Result Factory function New-CCRepoCheckResult { [CmdletBinding()] param( [Parameter(Mandatory)][string]$CheckId, [Parameter(Mandatory)][string]$Category, [Parameter(Mandatory)][string]$Item, [Parameter(Mandatory)][ValidateSet('Pass', 'Fail', 'Warning', 'Skipped')][string]$Status, [Parameter(Mandatory)][ValidateSet('Error', 'Warning', 'Info')][string]$Severity, [Parameter(Mandatory)][string]$Message, [string]$Standard = 'core', [bool]$FixAvailable = $false, [string]$FixAction = '' ) [PSCustomObject]@{ CheckId = $CheckId Category = $Category Item = $Item Status = $Status Severity = $Severity Message = $Message FixAvailable = $FixAvailable Standard = $Standard FixAction = $FixAction } } #endregion #region Config Loading function ConvertFrom-CCRepoYaml { [CmdletBinding()] param([Parameter(Mandatory)][string]$Content) $result = @{} $currentSection = $null foreach ($line in $Content -split "`n") { $line = $line.TrimEnd() if ($line -match '^\s*#' -or $line -match '^\s*$') { continue } if ($line -match '^(\w[\w_]*):\s*$') { $currentSection = $Matches[1] $result[$currentSection] = @{} } elseif ($line -match '^(\w[\w_]*):\s+(.+)$') { $key = $Matches[1] $value = $Matches[2].Trim() if ($value -eq 'true') { $value = $true } elseif ($value -eq 'false') { $value = $false } elseif ($value -match '^\d+$') { $value = [int]$value } $result[$key] = $value } elseif ($line -match '^\s+(\w[\w_]*):\s+(.+)$' -and $currentSection) { $key = $Matches[1] $value = $Matches[2].Trim() if ($value -eq 'true') { $value = $true } elseif ($value -eq 'false') { $value = $false } elseif ($value -match '^\d+$') { $value = [int]$value } $result[$currentSection][$key] = $value } elseif ($line -match '^\s+-\s+(.+)$') { $value = $Matches[1].Trim() if ($currentSection -and $result[$currentSection] -is [hashtable]) { # Find last key in current section that should be an array $lastKey = $result[$currentSection].Keys | Select-Object -Last 1 if ($lastKey -and $result[$currentSection][$lastKey] -isnot [array]) { $result[$currentSection][$lastKey] = @($result[$currentSection][$lastKey]) } if ($lastKey) { $result[$currentSection][$lastKey] += $value } } elseif (-not $currentSection) { $lastKey = $result.Keys | Where-Object { $result[$_] -is [array] -or $result[$_] -is [string] } | Select-Object -Last 1 if ($lastKey) { if ($result[$lastKey] -isnot [array]) { $result[$lastKey] = @() } $result[$lastKey] += $value } } } } $result } function Get-CCRepoConfig { [CmdletBinding()] param( [Parameter(Mandatory)][string]$Path, [string]$Standard = 'core', [string]$ConfigFile ) if (-not $script:Standards.ContainsKey($Standard)) { throw "Unknown standard: '$Standard'. Valid values: $($script:Standards.Keys -join ', ')" } $config = $script:Standards[$Standard].Clone() # Load per-repo config if it exists $repoConfig = $null if ($ConfigFile -and (Test-Path $ConfigFile)) { $content = Get-Content $ConfigFile -Raw $repoConfig = ConvertFrom-CCRepoYaml -Content $content } elseif (Test-Path (Join-Path $Path '.codecompass/config.yml')) { $content = Get-Content (Join-Path $Path '.codecompass/config.yml') -Raw $repoConfig = ConvertFrom-CCRepoYaml -Content $content } if ($repoConfig) { # Apply structure overrides if ($repoConfig.ContainsKey('structure')) { if ($repoConfig['structure'].ContainsKey('source')) { $config['structure_source'] = $repoConfig['structure']['source'] } if ($repoConfig['structure'].ContainsKey('tests')) { $config['structure_tests'] = $repoConfig['structure']['tests'] } if ($repoConfig['structure'].ContainsKey('docs')) { $config['structure_docs'] = $repoConfig['structure']['docs'] } } # Apply disabled checks if ($repoConfig.ContainsKey('checks') -and $repoConfig['checks'].ContainsKey('disabled')) { $config['disabled_checks'] = $repoConfig['checks']['disabled'] } # Apply additional required files if ($repoConfig.ContainsKey('files') -and $repoConfig['files'].ContainsKey('required')) { $additional = $repoConfig['files']['required'] if ($additional -is [array]) { $config['files_required'] = $config['files_required'] + $additional } } } $config } #endregion #region Technology Detection function Get-CCTechStack { [CmdletBinding()] param([Parameter(Mandatory)][string]$Path) $detected = @() if ((Get-ChildItem $Path -Filter '*.sln' -ErrorAction SilentlyContinue) -or (Get-ChildItem $Path -Filter '*.csproj' -Recurse -Depth 2 -ErrorAction SilentlyContinue)) { $detected += 'dotnet' } if ((Get-ChildItem $Path -Filter '*.psd1' -ErrorAction SilentlyContinue) -or (Get-ChildItem $Path -Filter '*.psm1' -ErrorAction SilentlyContinue)) { $detected += 'powershell' } if (Test-Path (Join-Path $Path 'package.json')) { $detected += 'javascript' } if ((Test-Path (Join-Path $Path 'pyproject.toml')) -or (Test-Path (Join-Path $Path 'requirements.txt'))) { $detected += 'python' } if ((Get-ChildItem $Path -Filter '*.xcodeproj' -ErrorAction SilentlyContinue) -or (Test-Path (Join-Path $Path 'Podfile'))) { $detected += 'ios' } if (Test-Path (Join-Path $Path 'build.gradle')) { $detected += 'android' } $detected } #endregion |