Public/Test-SLRepo.ps1
|
function Test-SLRepo { <# .SYNOPSIS Checks git repos against the .gitattributes line-ending standard. .DESCRIPTION Finds git repos at or under -Path and, for each one, compares its line-ending rules (the .gitattributes file) with the right profile, and counts committed files that still need 'git add --renormalize .'. Status values: MATCHES every profile rule is there, with the same attributes, nothing else EXTENDS every profile rule is there, plus repo-specific rules (fine) DIFFERS a profile rule is missing, or a pattern has different attributes MISSING no .gitattributes A repo is OK when it MATCHES or EXTENDS and no file needs renormalizing. Files are compared by meaning: comments, blank lines, spacing and order don't matter, and 'binary' equals '-diff -merge -text'. Profiles: docs, powershell, dotnet (built in), plus any defined in the standard's GitAttributes section. Which one a repo gets, first match wins: 1. -Profile 2. the repo's folder name pinned in the standard (GitAttributes.Repos) 3. dotnet: a .sln, .slnx, .csproj or .fsproj within 3 folders 4. powershell: a module manifest (.psd1 with RootModule or ModuleVersion) within 2 5. docs: anything else The standard is optional here. It is found like Get-SecondLineStandard finds it (-Standard, SECONDLINE_STANDARD, secondline.standard.psd1 in the current folder); if none is found, the built-in profiles are used. Read-only: nothing is changed. Set-SLGitAttributes fixes what this finds. .PARAMETER Path A repo, or a folder of repos. Default: the current folder. .PARAMETER Depth How many folders below -Path to look for repos. Default: 2. A repo's own subfolders are never searched, and folders named node_modules, bin, obj, packages and .git are skipped. .PARAMETER Profile Use this profile for every repo found (overrides pins and detection). .PARAMETER Standard The standard file. See Get-SecondLineStandard. .PARAMETER PassThru Return one result object per repo instead of $true/$false. .PARAMETER Quiet Don't print the report. .OUTPUTS Without -PassThru: $true if every repo is OK, else $false (also when no repo is found). With -PassThru: one object per repo with Repo, Path, Profile, ProfileReason, Status, MissingRules, ConflictingRules, ExtraRules, FilesToRenormalize, RenormalizeSample and Note. .EXAMPLE Test-SLRepo -Path C:\src Checks every repo up to two folders under C:\src. .EXAMPLE Test-SLRepo -Path C:\src\MyApp -Profile dotnet .EXAMPLE Test-SLRepo C:\src -PassThru -Quiet | Where-Object Status -eq 'DIFFERS' .EXAMPLE Test-SLRepo C:\src -PassThru -Quiet | Where-Object Status -eq 'MISSING' | Set-SLGitAttributes -WhatIf #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', '', Justification = '-Profile is the documented parameter name; it shadows $PROFILE only inside this function, which never uses it.')] [CmdletBinding()] [OutputType([bool], [pscustomobject])] param( [Parameter(Position = 0)] [string] $Path, [ValidateRange(0, 10)] [int] $Depth = 2, [string] $Profile, [string] $Standard, [switch] $PassThru, [switch] $Quiet ) [void] (Get-SLGitPath) # fail early, with an install hint, if git is missing if (-not $Path) { $Path = (Get-Location).ProviderPath } if (-not (Test-Path -LiteralPath $Path -PathType Container)) { throw "Folder not found: $Path. Pass a repo, or a folder that contains repos." } $Path = (Resolve-Path -LiteralPath $Path).ProviderPath $std = Get-SLOptionalStandard -Path $Standard $profiles = Get-SLProfiles -Standard $std if ($Profile -and -not $profiles.ContainsKey($Profile)) { throw "Unknown profile '$Profile'. Use one of: $((@($profiles.Keys) | Sort-Object) -join ', ')." } $repos = @(Find-SLGitRepo -Path $Path -Depth $Depth) if ($repos.Count -eq 0) { Write-Warning "No git repos found in $Path or up to $Depth folder(s) below it. Point -Path at a repo or a folder of repos, or raise -Depth." } $checks = @() foreach ($repo in $repos) { $checks += Get-SLRepoCheck -RepoPath $repo -Standard $std -ProfileName $Profile -Profiles $profiles } if (-not $Quiet) { Write-SLRepoReport -Checks $checks -Path $Path -Standard $std } if ($PassThru) { return $checks } # Nothing checked is not a pass. return ($checks.Count -gt 0 -and @($checks | Where-Object { -not (Test-SLRepoOk $_) }).Count -eq 0) } |