Netscoot.Core/Public/Find-PathReference.ps1
|
function Find-PathReference { <# .SYNOPSIS Find references to a path in non-canonical, path-hardcoding files (build/CI/hook/ container scripts) that no first-party tool reconciles. report-only. .DESCRIPTION Moving a project/folder breaks any path hardcoded in build.ps1, CI YAML, git hooks, tools scripts, Makefile/Dockerfile, etc. - and unlike .sln/.csproj/.psd1 there is no tool that understands their schema, so they cannot be safely auto-rewritten (a blind regex could corrupt logic). This detects the class of such files (by location + name, not a hardcoded filename list) and reports lines that reference the given path, so you (or an agent) can fix them deliberately. It never edits anything. Two confidence tiers: High when the item's repository-relative path appears (e.g. 'lib/Tarragon.csproj' or 'lib\Tarragon.csproj'), Low when only the bare leaf name appears (e.g. 'Tarragon.csproj'), which is likely but not certain. Run it before a move (to see what will break) or after (searching the old path). .PARAMETER Path The item being/that was moved. Accepts pipeline input. .PARAMETER RepositoryRoot Root to scan. Defaults to the enclosing git repository root. .PARAMETER AdditionalGlob Extra repository-relative globs to include in the candidate set (e.g. 'deploy/*.sh'). .OUTPUTS Netscoot.PathReference - one per matching line. .EXAMPLE # Build/CI/hook lines that hardcode the path (report-only) Find-PathReference -Path ./lib/Tarragon.csproj # Scan the old path after a move to find what still points at it Find-PathReference -Path ./libs/Tarragon/Tarragon.csproj # Widen the candidate set with extra repository-relative globs Find-PathReference -Path ./lib/Tarragon.csproj -AdditionalGlob 'deploy/*.sh','*.psake.ps1' #> [CmdletBinding()] [OutputType('Netscoot.PathReference')] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('FullName', 'PSPath')] [ValidateNotNullOrEmpty()] [string]$Path, [string]$RepositoryRoot, [string[]]$AdditionalGlob = @() ) process { $target = Resolve-FullPath $Path if (-not $RepositoryRoot) { $start = if (Test-Path -LiteralPath $target -PathType Container) { $target } else { Split-Path -Parent $target } $RepositoryRoot = Get-RepositoryRoot -StartPath $start } $root = (Resolve-FullPath $RepositoryRoot).TrimEnd('\', '/') $rel = ($target.Substring($root.Length).TrimStart('\', '/')) $relFwd = $rel.Replace('\', '/') $relBack = $rel.Replace('/', '\') $leaf = Split-Path -Leaf $target # The High check is a case-insensitive substring test (IndexOf, no regex). The Low check needs # word-boundary lookarounds, so build that regex once here (case-insensitive, as -match was) # rather than per line. $ci = [System.StringComparison]::OrdinalIgnoreCase $leafRegex = [regex]::new('(?<![\w.])' + [regex]::Escape($leaf) + '(?![\w])', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase) $hits = 0 foreach ($file in (Get-PathBearingFile -RepositoryRoot $root -AdditionalGlob $AdditionalGlob)) { $n = 0 foreach ($line in (Get-Content -LiteralPath $file.FullName -ErrorAction SilentlyContinue)) { $n++ $confidence = $null if ($line.IndexOf($relFwd, $ci) -ge 0 -or ($relBack -ne $relFwd -and $line.IndexOf($relBack, $ci) -ge 0)) { $confidence = 'High' } elseif ($leafRegex.IsMatch($line)) { $confidence = 'Low' } if ($confidence) { $hits++ [pscustomobject]@{ PSTypeName = 'Netscoot.PathReference' File = $file.FullName Line = $n Confidence = $confidence Text = $line.Trim() } } } } if ($hits -gt 0) { Write-Warning "$hits path-bearing reference(s) to '$leaf' found in non-canonical files (build/CI/hooks/etc.). These are not auto-reconciled - review and fix them by hand." } } } |