Automate BusinessMgr validation

This commit is contained in:
Andy Howard
2026-09-12 17:00:47 +01:00
parent f7d06e9533
commit 7abc5d0126
5 changed files with 151 additions and 3 deletions
+4
View File
@@ -18,6 +18,10 @@ dotnet run --project SaBusinessMgrCoreWebControl
dotnet run --project SaBusinessMgrCoreWebPortal
```
For the standard repository validation, run `./eng/validate.ps1`. It selects Visual Studio MSBuild for the SSDT-dependent Portal test project, runs both test assemblies, and performs repository checks; see `eng/README.md`.
When validation requirements, solution structure, target frameworks, test projects, package-audit scope, or required tooling change, update `eng/validate.ps1` and `eng/README.md` as part of the same change. Run the updated script before handoff. Do not bypass a failing validation stage without documenting the reason.
Use `--configuration Release` for release validation. After code changes, run `dotnet build BusinessMgr.sln` where practical and report validation that could not be completed. Both launch profiles default to ports 5000/5001; override ports to run them together.
For changes to Common public APIs, target frameworks, packages, or shared behavior, validate the directly affected Common project before BusinessMgr. Coordinate framework upgrades across the dependency boundary. When the repositories cannot move atomically, consume a temporary compatible target from Common, migrate BusinessMgr, and remove the old Common target only after no in-scope consumer requires it.
+2 -1
View File
@@ -7,8 +7,9 @@ table td {
}
</style>
Last updated: 2026-09-10
Last updated: 2026-09-12
| Component | Scope | Status | Date reviewed | Next steps | Notes |
|---|---|---|---|---|---|
| `BusinessMgr.sln` | .NET 10 restore, Debug build, and tests | Migrated; passing with warnings and toolchain requirement | 2026-09-10 | Review vulnerable and framework-specific packages, perform application smoke tests, then release-validate before removing Common's temporary .NET 8 targets. | All eight projects target `net10.0`. The complete solution builds with Visual Studio 18 MSBuild; the .NET CLI cannot build `SaBusMgrTest_Portal` because its SSDT SQL unit-testing targets are Visual Studio-specific (`MSB4278`). Control tests pass 8 of 8 and Portal tests pass 13 of 13. |
| `eng/validate.ps1` | Repository validation | Complete; passing | 2026-09-12 | Use as the standard validation entry point and extend it when repository-wide checks change. | Locates Visual Studio MSBuild for the SSDT project, builds serially, runs 8 Control and 13 Portal tests, and verifies changed-file line endings and diffs. |
+3 -2
View File
@@ -7,13 +7,13 @@ table td {
}
</style>
Last updated: 2026-09-10
Last updated: 2026-09-12
## Prioritised Topics to Be Actioned
| Priority | Topic | Date noted | Next step | Notes |
|---|---|---|---|---|
| P1 | Remove vulnerable transitive `System.Security.Cryptography.Xml` 8.0.2 dependency | 2026-09-10 | Upgrade or replace the legacy ASP.NET Core Identity 2.3.1 dependency and NPOI 2.6.2 path, then confirm that restore no longer resolves the vulnerable package. | .NET 10 restore reports multiple `NU1903` high-severity advisories. The package enters through `Microsoft.AspNetCore.Identity` 2.3.1 and through NPOI 2.6.2 in Common's `SacsStandardProc`. Coordinate the Common NPOI path with that repository. |
| P1 | Remove vulnerable transitive `System.Security.Cryptography.Xml` 8.0.2 dependency | 2026-09-10 | Upgrade or replace the legacy ASP.NET Core Identity 2.3.1 dependency, then confirm restore is clean. | The Common NPOI path is complete: NPOI 2.7.6 plus an explicit `System.Security.Cryptography.Xml` 10.0.12 reference passes the spreadsheet, Common, and BusinessMgr suites. BusinessMgr still resolves version 8.0.2 independently through `Microsoft.AspNetCore.DataProtection` 2.3.0 from ASP.NET Core Identity 2.3.1, producing the remaining `NU1903` advisories. |
| P2 | Review framework-specific package versions after .NET 10 retargeting | 2026-09-10 | Review ASP.NET Core Identity and Razor runtime compilation, ODBC, and System.Drawing packages for supported .NET 10 versions and behavior before release validation. | Existing package versions compile and all tests pass, but several remain on 2.x or 6.x generations and should not be assumed to be fully supported merely because compilation succeeds. |
## .NET 10 Dependencies and Sequence
@@ -38,3 +38,4 @@ Last updated: 2026-09-10
| Capture the pre-migration BusinessMgr build and test baseline | 2026-09-10 | 2026-09-10 | 2026-09-10 | Using SDK 10.0.401, restore succeeded. Visual Studio 18 MSBuild built the complete Debug solution successfully, including the SSDT-dependent Portal test project. The .NET CLI built all production projects and the Control tests but reports `MSB4278` for the Portal test project's Visual Studio SSDT import, so Visual Studio MSBuild remains required for that project. Common's MailKit 4.13.0 dependency reports `NU1902`; the CLI build otherwise recorded 20 compiler warnings. Control tests passed 8 of 8 and Portal tests passed 13 of 13. |
| Pin the BusinessMgr .NET 10 SDK | 2026-09-10 | 2026-09-10 | 2026-09-10 | Added `global.json` for SDK 10.0.401 with `latestPatch` roll-forward and prerelease SDKs disabled. Visual Studio MSBuild remains required for the SSDT-dependent Portal test project. |
| Migrate BusinessMgr to .NET 10 against compatible Common targets | 2026-09-10 | 2026-09-10 | 2026-09-10 | All eight BusinessMgr projects now target `net10.0`. Visual Studio 18 MSBuild restored and built the complete solution successfully with no compilation errors. Control tests passed 8 of 8 and Portal tests passed 13 of 13 on .NET 10. Package vulnerability and framework-specific package reviews are tracked separately. |
| Automate BusinessMgr validation | 2026-09-12 | 2026-09-12 | 2026-09-12 | Added `eng/validate.ps1` to locate Visual Studio MSBuild, build the SSDT-dependent solution serially, run both test assemblies, and verify diffs and changed-file line endings. The script passes with 8 of 8 Control and 13 of 13 Portal tests. |
+9
View File
@@ -0,0 +1,9 @@
# Validation
Run the complete BusinessMgr validation:
```powershell
.\eng\validate.ps1
```
Use `-SkipRestore` when packages are already restored. The script suppresses .NET first-run setup and banners, locates Visual Studio MSBuild because the Portal test project requires Visual Studio SSDT targets, builds serially, derives test output paths from each project's target framework, runs both test assemblies, stores test output under the ignored `TestResults/Validation` folder, checks the Git diff, and verifies working-tree line endings without modifying files.
+133
View File
@@ -0,0 +1,133 @@
[CmdletBinding()]
param(
[switch] $SkipRestore
)
$ErrorActionPreference = 'Stop'
$repositoryRoot = Split-Path -Parent $PSScriptRoot
$testResultsPath = Join-Path $repositoryRoot 'TestResults\Validation'
$dotnetHome = Join-Path (Split-Path -Parent $repositoryRoot) '.dotnet-cli-validation'
$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'
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 Get-VisualStudioMsBuild
{
$vsWhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
if (Test-Path -LiteralPath $vsWhere -PathType Leaf)
{
$foundPath = & $vsWhere -latest -products * -requires Microsoft.Component.MSBuild -find 'MSBuild\**\Bin\MSBuild.exe' | Select-Object -First 1
if ($foundPath) { return $foundPath }
}
$candidates = @(
'C:\Program Files\Microsoft Visual Studio\18\Enterprise\MSBuild\Current\Bin\MSBuild.exe',
'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe',
'C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe',
'C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe'
)
$foundPath = $candidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
if ($foundPath) { return $foundPath }
throw 'Visual Studio MSBuild was not found. BusinessMgr requires its SSDT SQL unit-testing targets.'
}
function Get-TestAssemblyPath
{
param(
[Parameter(Mandatory)] [string] $ProjectPath,
[Parameter(Mandatory)] [string] $AssemblyName
)
$projectFullPath = Join-Path $repositoryRoot $ProjectPath
[xml] $projectXml = Get-Content -LiteralPath $projectFullPath -Raw
$targetFrameworkNode = $projectXml.SelectSingleNode('//TargetFramework')
if ($null -eq $targetFrameworkNode)
{
throw "A single TargetFramework was not found in $ProjectPath"
}
return Join-Path (Split-Path -Parent $projectFullPath) "bin\Debug\$($targetFrameworkNode.InnerText)\$AssemblyName.dll"
}
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:`n$($invalidFiles -join "`n")"
}
}
Push-Location $repositoryRoot
try
{
Write-Host 'Validating BusinessMgr workspace...'
$msBuildPath = Get-VisualStudioMsBuild
$msBuildArguments = @('BusinessMgr.sln', '/p:Configuration=Debug', '/m:1', '/v:minimal', '/nologo')
if (-not $SkipRestore) { $msBuildArguments += '/restore' }
Invoke-External $msBuildPath $msBuildArguments
$controlAssembly = Get-TestAssemblyPath 'SaBusMgrTest_Control\SaBusMgrTest_Control.csproj' 'SaBusMgrTest_Control'
$portalAssembly = Get-TestAssemblyPath 'SaBusMgrTest_Portal\SaBusMgrTest_Portal.csproj' 'SaBusMgrTest_Portal'
Invoke-External dotnet @('vstest', $controlAssembly, "--TestAdapterPath:$(Split-Path -Parent $controlAssembly)", "--ResultsDirectory:$testResultsPath")
Invoke-External dotnet @('vstest', $portalAssembly, "--TestAdapterPath:$(Split-Path -Parent $portalAssembly)", "--ResultsDirectory:$testResultsPath")
Invoke-External git @('diff', '--check')
Test-LineEndings
Write-Host 'BusinessMgr validation passed.' -ForegroundColor Green
}
finally
{
Pop-Location
}