[CmdletBinding()] param( [switch] $SkipRestore, [switch] $CollectCoverage ) $ErrorActionPreference = 'Stop' $repositoryRoot = Split-Path -Parent $PSScriptRoot $testResultsPath = Join-Path $repositoryRoot 'TestResults\Validation' $dotnetHome = Join-Path (Split-Path -Parent $repositoryRoot) '.dotnet-cli-validation' $nugetPackagesPath = if ($env:NUGET_PACKAGES) { $env:NUGET_PACKAGES } else { Join-Path $env:USERPROFILE '.nuget\packages' } $env:DOTNET_CLI_HOME = $dotnetHome $env:DOTNET_GENERATE_ASPNET_CERTIFICATE = 'false' $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1' $env:DOTNET_CLI_TELEMETRY_OPTOUT = '1' $env:DOTNET_NOLOGO = '1' $env:NUGET_PACKAGES = $nugetPackagesPath function Invoke-External { param( [Parameter(Mandatory)] [string] $FilePath, [Parameter(Mandatory)] [string[]] $ArgumentList ) & $FilePath @ArgumentList if ($LASTEXITCODE -ne 0) { throw "Command failed with exit code ${LASTEXITCODE}: $FilePath $($ArgumentList -join ' ')" } } function Test-LineEndings { $crlfExtensions = @('.cs', '.csproj', '.sln', '.props', '.targets', '.config', '.json', '.xml', '.md', '.txt', '.ps1', '.cmd', '.bat') $lfExtensions = @('.svg', '.html', '.css', '.js', '.sh', '.yml', '.yaml') $invalidFiles = [System.Collections.Generic.List[string]]::new() $files = @(& git -C $repositoryRoot diff --name-only --diff-filter=ACMRT) if ($LASTEXITCODE -ne 0) { throw 'Unable to enumerate modified repository files.' } $files += @(& git -C $repositoryRoot diff --cached --name-only --diff-filter=ACMRT) if ($LASTEXITCODE -ne 0) { throw 'Unable to enumerate staged repository files.' } $files += @(& git -C $repositoryRoot ls-files --others --exclude-standard) if ($LASTEXITCODE -ne 0) { throw 'Unable to enumerate untracked repository files.' } $files = $files | Sort-Object -Unique foreach ($relativePath in $files) { $extension = [System.IO.Path]::GetExtension($relativePath).ToLowerInvariant() if (($extension -notin $crlfExtensions) -and ($extension -notin $lfExtensions)) { continue } $fullPath = Join-Path $repositoryRoot $relativePath if (-not (Test-Path -LiteralPath $fullPath -PathType Leaf)) { continue } $bytes = [System.IO.File]::ReadAllBytes($fullPath) for ($index = 0; $index -lt $bytes.Length; $index++) { if (($extension -in $crlfExtensions) -and ($bytes[$index] -eq 10) -and (($index -eq 0) -or ($bytes[$index - 1] -ne 13))) { $invalidFiles.Add($relativePath) break } if (($extension -in $lfExtensions) -and ($bytes[$index] -eq 13)) { $invalidFiles.Add($relativePath) break } } } if ($invalidFiles.Count -gt 0) { throw "Files with incorrect line endings: $($invalidFiles -join " ")" } } function Test-CoverageReport { param([Parameter(Mandatory)] [string] $ResultsPath) $report = Get-ChildItem -LiteralPath $ResultsPath -Filter 'coverage.cobertura.xml' -Recurse | Select-Object -First 1 if (($null -eq $report) -or ($report.Length -eq 0)) { throw "A non-empty Cobertura coverage report was not found under $ResultsPath" } [xml] $coverage = Get-Content -LiteralPath $report.FullName -Raw if ($coverage.DocumentElement.Name -ne 'coverage') { throw "The coverage report at $($report.FullName) is not valid Cobertura XML." } } Push-Location $repositoryRoot try { Write-Host 'Validating BusinessMgr workspace...' if (-not $SkipRestore) { Invoke-External dotnet @('restore', 'BusinessMgr.sln', '--disable-build-servers', '--verbosity', 'minimal') } Invoke-External dotnet @('build', 'BusinessMgr.sln', '--configuration', 'Debug', '--no-restore', '--disable-build-servers', '--verbosity', 'minimal', '--maxcpucount:1') Invoke-External dotnet @('test', 'SaBusMgrTest_Control\SaBusMgrTest_Control.csproj', '--configuration', 'Debug', '--no-build', '--no-restore', '--results-directory', $testResultsPath, '--verbosity', 'minimal') Invoke-External dotnet @('test', 'SaBusMgrTest_Portal\SaBusMgrTest_Portal.csproj', '--configuration', 'Debug', '--no-build', '--no-restore', '--results-directory', $testResultsPath, '--verbosity', 'minimal') if ($CollectCoverage) { $coverageRunPath = Join-Path $testResultsPath "Coverage\$([guid]::NewGuid())" $controlCoveragePath = Join-Path $coverageRunPath 'Control' $portalCoveragePath = Join-Path $coverageRunPath 'Portal' Invoke-External dotnet @('test', 'SaBusMgrTest_Control\SaBusMgrTest_Control.csproj', '--configuration', 'Debug', '--no-build', '--no-restore', '--collect', 'XPlat Code Coverage', '--results-directory', $controlCoveragePath, '--verbosity', 'minimal') Test-CoverageReport $controlCoveragePath Invoke-External dotnet @('test', 'SaBusMgrTest_Portal\SaBusMgrTest_Portal.csproj', '--configuration', 'Debug', '--no-build', '--no-restore', '--collect', 'XPlat Code Coverage', '--results-directory', $portalCoveragePath, '--verbosity', 'minimal') Test-CoverageReport $portalCoveragePath } Invoke-External dotnet @('list', 'SaBusMgrStandardBase\SaBusMgrStandardBase.csproj', 'package', '--vulnerable', '--include-transitive') Invoke-External dotnet @('list', 'SaBusMgrCoreProcs\SaBusMgrCoreProcs.csproj', 'package', '--vulnerable', '--include-transitive') Invoke-External dotnet @('list', 'SaBusMgrCoreWeb\SaBusMgrCoreWeb.csproj', 'package', '--vulnerable', '--include-transitive') Invoke-External dotnet @('list', 'SaBusinessMgrCoreWebControl\SaBusinessMgrCoreWebControl.csproj', 'package', '--vulnerable', '--include-transitive') Invoke-External dotnet @('list', 'SaBusinessMgrCoreWebPortal\SaBusinessMgrCoreWebPortal.csproj', 'package', '--vulnerable', '--include-transitive') Invoke-External git @('diff', '--check') Test-LineEndings Write-Host 'BusinessMgr validation passed.' -ForegroundColor Green } finally { Pop-Location }