Public/Test-SecondLine.ps1
|
function Test-SecondLine { <# .SYNOPSIS Audits this machine against a SecondLine standard. .DESCRIPTION For each tool in the standard: finds it on PATH, reads its version, and checks it against the expected version and rule. Status values: OK found, and the version satisfies the rule MISMATCH found, but the version does not satisfy the rule MISSING not found on PATH UNKNOWN found, but no version could be read from its output Read-only: nothing is installed or changed. .PARAMETER Path The standard file. See Get-SecondLineStandard for where it is looked for when omitted. .PARAMETER PassThru Return one result object per tool (for scripts and CI) in addition to the report. .PARAMETER Quiet Don't print the report. Combine with -PassThru for results only. .PARAMETER Strict Throw if any tool is not OK. Useful in CI. .EXAMPLE Test-SecondLine .EXAMPLE Test-SecondLine -Path .\secondline.standard.psd1 -PassThru -Quiet | Where-Object Status -ne 'OK' #> [CmdletBinding()] param( [string] $Path, [switch] $PassThru, [switch] $Quiet, [switch] $Strict ) $standard = Get-SecondLineStandard -Path $Path $results = @() foreach ($tool in $standard.Tools) { $result = [pscustomobject]@{ PSTypeName = 'SecondLine.Result' Name = $tool.Name Status = '' Found = '' Expect = $tool.Expect Rule = $tool.Rule Path = '' Note = '' Install = $tool.Install } $cmd = Get-Command -Name $tool.Command -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $cmd) { $result.Status = 'MISSING' $result.Note = "'$($tool.Command)' is not on PATH." $results += $result continue } $result.Path = $cmd.Path $run = Invoke-SLTool -Path $cmd.Path -Arguments $tool.Arguments -TimeoutSeconds $tool.TimeoutSeconds if ($run.Error) { $result.Status = 'UNKNOWN' $result.Note = "Could not run: $($run.Error)" $results += $result continue } $notes = @() if ($run.TimedOut) { $notes += "did not exit within $($tool.TimeoutSeconds)s and was stopped" } $all = @(Get-SLVersionTexts -Text $run.Output -Pattern $tool.VersionPattern) if ($all.Count -eq 0) { $result.Status = 'UNKNOWN' $notes += "no version found in output of '$($tool.Command) $($tool.Arguments)'" } elseif ($tool.Match -eq 'Any') { # Several versions installed side by side (e.g. dotnet --list-sdks). # OK if any satisfies the rule; prefer a release over a prerelease, # then the highest. $passing = @($all | Where-Object { Test-SLVersionRule -Found $_ -Expect $tool.Expect -Rule $tool.Rule }) $pick = Select-SLBestVersion $passing if ($pick) { $result.Status = 'OK' $result.Found = $pick } else { $result.Status = 'MISMATCH' $result.Found = Select-SLBestVersion $all } $notes += "installed: $($all -join ', ')" } else { $result.Found = $all[0] if (Test-SLVersionRule -Found $all[0] -Expect $tool.Expect -Rule $tool.Rule) { $result.Status = 'OK' } else { $result.Status = 'MISMATCH' } } if ($result.Found -and (Test-SLPrerelease $result.Found)) { $notes += 'this is a prerelease build' } $result.Note = ($notes -join '; ') $results += $result } if (-not $Quiet) { Write-SLReport -Standard $standard -Results $results } if ($PassThru) { $results } if ($Strict) { $bad = @($results | Where-Object { $_.Status -ne 'OK' }) if ($bad.Count -gt 0) { throw "SecondLine: $($bad.Count) of $($results.Count) tools are not OK: $(($bad | ForEach-Object { "$($_.Name) ($($_.Status))" }) -join ', ')" } } } |