From f4af340bba790b74247c5014bc6dd94cabc8ba9f Mon Sep 17 00:00:00 2001 From: Chrissy LeMaire Date: Sat, 2 May 2026 21:04:03 +0200 Subject: [PATCH 1/4] fix: resolve SqlClient runtime asset selection (#50) --- .github/workflows/build-release.yml | 33 +++++ .github/workflows/build.yml | 26 ++++ build/build.ps1 | 19 ++- dbatools.library.psm1 | 214 ++++++++++++++++++++++++++-- 4 files changed, 277 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 1fc0552d..eff70cd6 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -201,6 +201,39 @@ jobs: New-Item -ItemType Directory -Path /tmp/DbatoolsExport -Force | Out-Null } + - name: Verify SqlClient runtime asset selection + shell: pwsh + run: | + Import-Module ./artifacts/dbatools.library/dbatools.library.psd1 -Force + + $sqlClient = [System.AppDomain]::CurrentDomain.GetAssemblies() | + Where-Object { $_.GetName().Name -eq 'Microsoft.Data.SqlClient' } | + Select-Object -First 1 + + if (-not $sqlClient) { + throw "Microsoft.Data.SqlClient was not loaded" + } + + $location = $sqlClient.Location.Replace('/', '\') + $expected = if ($IsWindows) { + "runtimes\win\lib\net8.0\Microsoft.Data.SqlClient.dll" + } else { + "runtimes\unix\lib\net8.0\Microsoft.Data.SqlClient.dll" + } + + if ($location -notlike "*$expected") { + throw "Microsoft.Data.SqlClient loaded from unexpected path: $($sqlClient.Location)" + } + + if ($IsWindows) { + $sniPath = "./artifacts/dbatools.library/core/lib/runtimes/win-x64/native/Microsoft.Data.SqlClient.SNI.dll" + if (-not (Test-Path $sniPath)) { + throw "Missing Windows x64 SqlClient native SNI DLL at $sniPath" + } + } + + Write-Host "SqlClient runtime asset verified: $($sqlClient.Location)" + - name: Install SqlPackagetests shell: pwsh run: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d4b3a0d..eeb2c9cb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -117,6 +117,32 @@ jobs: Write-Host "Loaded dbatools.library version: $($module.Version)" Write-Host "Module path: $($module.Path)" + - name: Verify SqlClient runtime asset selection + shell: pwsh + run: | + Import-Module ./artifacts/dbatools.library/dbatools.library.psd1 -Force + + $sqlClient = [System.AppDomain]::CurrentDomain.GetAssemblies() | + Where-Object { $_.GetName().Name -eq 'Microsoft.Data.SqlClient' } | + Select-Object -First 1 + + if (-not $sqlClient) { + throw "Microsoft.Data.SqlClient was not loaded" + } + + $location = $sqlClient.Location.Replace('/', '\') + $expected = "runtimes\win\lib\net8.0\Microsoft.Data.SqlClient.dll" + if ($location -notlike "*$expected") { + throw "Microsoft.Data.SqlClient loaded from unexpected path: $($sqlClient.Location)" + } + + $sniPath = "./artifacts/dbatools.library/core/lib/runtimes/win-x64/native/Microsoft.Data.SqlClient.SNI.dll" + if (-not (Test-Path $sniPath)) { + throw "Missing Windows x64 SqlClient native SNI DLL at $sniPath" + } + + Write-Host "SqlClient runtime asset verified: $($sqlClient.Location)" + - name: Test AvoidConflicts with SqlServer module (Windows PowerShell 5.1) shell: powershell run: | diff --git a/build/build.ps1 b/build/build.ps1 index deaf134a..0a937275 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -134,8 +134,6 @@ if (Test-Path $coreRuntimesPath) { } } -Copy-Item (Join-Path $libPath "core\lib\runtimes\unix\lib\net8.0\Microsoft.Data.SqlClient.dll") -Destination (Join-Path $libPath "core/lib/") -Force - if ($CoreOnly) { Write-Host "CoreOnly specified - returning after core build" return @@ -296,6 +294,8 @@ $validationErrors = @() # Check for critical runtime dependencies $criticalFiles = @( @{Path = "core\lib\runtimes\win-x64\native\Microsoft.Data.SqlClient.SNI.dll"; Description = ".NET Core SNI DLL (Windows x64)"}, + @{Path = "core\lib\runtimes\win\lib\net8.0\Microsoft.Data.SqlClient.dll"; Description = ".NET Core SqlClient (Windows runtime asset)"}, + @{Path = "core\lib\runtimes\unix\lib\net8.0\Microsoft.Data.SqlClient.dll"; Description = ".NET Core SqlClient (Unix runtime asset)"}, @{Path = "core\lib\Microsoft.Data.SqlClient.dll"; Description = ".NET Core SqlClient"}, @{Path = "desktop\lib\Microsoft.Data.SqlClient.dll"; Description = ".NET Framework SqlClient"}, @{Path = "core\lib\dbatools.dll"; Description = ".NET Core dbatools assembly"}, @@ -312,6 +312,19 @@ foreach ($file in $criticalFiles) { } } +$coreSqlClientPath = Join-Path $libPath "core\lib\Microsoft.Data.SqlClient.dll" +$coreUnixSqlClientPath = Join-Path $libPath "core\lib\runtimes\unix\lib\net8.0\Microsoft.Data.SqlClient.dll" +if ((Test-Path $coreSqlClientPath) -and (Test-Path $coreUnixSqlClientPath)) { + $coreSqlClientHash = (Get-FileHash -Path $coreSqlClientPath -Algorithm SHA256).Hash + $coreUnixSqlClientHash = (Get-FileHash -Path $coreUnixSqlClientPath -Algorithm SHA256).Hash + if ($coreSqlClientHash -eq $coreUnixSqlClientHash) { + Write-Host "[ERROR] core\lib\Microsoft.Data.SqlClient.dll matches the Unix runtime asset" -ForegroundColor Red + $validationErrors += "Core SqlClient was overwritten with the Unix runtime asset" + } else { + Write-Host "[OK] Core SqlClient root assembly was not overwritten with the Unix runtime asset" -ForegroundColor Green + } +} + # Check if runtimes folder structure exists if (Test-Path (Join-Path $libPath "core\lib\runtimes")) { Write-Host "[OK] Runtimes folder structure preserved" -ForegroundColor Green @@ -332,4 +345,4 @@ if ($env:GITHUB_ACTIONS -and $LASTEXITCODE -eq 0) { exit 0 } else { exit $LASTEXITCODE -} \ No newline at end of file +} diff --git a/dbatools.library.psm1 b/dbatools.library.psm1 index 99cdacd7..e7c41634 100644 --- a/dbatools.library.psm1 +++ b/dbatools.library.psm1 @@ -96,8 +96,10 @@ if ($PSVersionTable.PSEdition -ne "Core") { if (-not ("CoreRedirector" -as [type])) { $coreSource = @" using System; + using System.Collections.Generic; using System.IO; using System.Reflection; + using System.Runtime.InteropServices; using System.Runtime.Loader; public class CoreRedirector @@ -110,6 +112,7 @@ if ($PSVersionTable.PSEdition -ne "Core") { if (_registered) return; _libPath = libPath; AssemblyLoadContext.Default.Resolving += OnResolving; + AssemblyLoadContext.Default.ResolvingUnmanagedDll += OnResolvingUnmanagedDll; _registered = true; } @@ -134,27 +137,163 @@ if ($PSVersionTable.PSEdition -ne "Core") { } } - // Try to load from our lib folder if the file exists - string dllPath = _libPath + name + ".dll"; - if (File.Exists(dllPath)) + foreach (string dllPath in GetManagedAssemblyPaths(name)) { - try + if (File.Exists(dllPath)) { - return AssemblyLoadContext.Default.LoadFromAssemblyPath(dllPath); + try + { + return AssemblyLoadContext.Default.LoadFromAssemblyPath(dllPath); + } + catch + { + // Failed to load, try the next candidate + } } - catch + } + + return null; + } + + private static IntPtr OnResolvingUnmanagedDll(Assembly assembly, string libraryName) + { + string architectureRid = GetArchitectureRid(); + if (String.IsNullOrEmpty(architectureRid)) + { + return IntPtr.Zero; + } + + foreach (string fileName in GetNativeLibraryNames(libraryName)) + { + string nativePath = Path.Combine(_libPath, "runtimes", architectureRid, "native", fileName); + if (File.Exists(nativePath)) { - // Failed to load, return null to let default resolution continue + try + { + return NativeLibrary.Load(nativePath); + } + catch + { + // Failed to load, try the next candidate + } } } - return null; + return IntPtr.Zero; + } + + private static string[] GetManagedAssemblyPaths(string name) + { + string fileName = name + ".dll"; + string platformRid = GetPlatformRid(); + string architectureRid = GetArchitectureRid(); + + var paths = new List(); + paths.Add(Path.Combine(_libPath, fileName)); + + if (!String.IsNullOrEmpty(platformRid)) + { + paths.Add(Path.Combine(_libPath, "runtimes", platformRid, "lib", "net8.0", fileName)); + paths.Add(Path.Combine(_libPath, "runtimes", platformRid, "lib", "netstandard1.6", fileName)); + } + + if (!String.IsNullOrEmpty(architectureRid)) + { + paths.Add(Path.Combine(_libPath, "runtimes", architectureRid, "lib", "net8.0", fileName)); + paths.Add(Path.Combine(_libPath, "runtimes", architectureRid, "lib", "netstandard1.6", fileName)); + } + + return paths.ToArray(); + } + + private static string[] GetNativeLibraryNames(string libraryName) + { + var names = new List(); + names.Add(libraryName); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (!libraryName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + { + names.Add(libraryName + ".dll"); + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + if (!libraryName.EndsWith(".dylib", StringComparison.OrdinalIgnoreCase)) + { + names.Add(libraryName + ".dylib"); + names.Add("lib" + libraryName + ".dylib"); + } + } + else + { + if (!libraryName.EndsWith(".so", StringComparison.OrdinalIgnoreCase)) + { + names.Add(libraryName + ".so"); + names.Add("lib" + libraryName + ".so"); + } + } + + return names.ToArray(); + } + + private static string GetPlatformRid() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return "win"; + } + + return "unix"; + } + + private static string GetArchitectureRid() + { + string osPart; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + osPart = "win"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + osPart = "linux"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + osPart = "osx"; + } + else + { + return null; + } + + string architecture; + switch (RuntimeInformation.ProcessArchitecture) + { + case Architecture.X86: + architecture = "x86"; + break; + case Architecture.X64: + architecture = "x64"; + break; + case Architecture.Arm: + architecture = "arm"; + break; + case Architecture.Arm64: + architecture = "arm64"; + break; + default: + return null; + } + + return osPart + "-" + architecture; } } "@ try { - $null = Add-Type -TypeDefinition $coreSource -ReferencedAssemblies 'System.Runtime.Loader' + $null = Add-Type -TypeDefinition $coreSource -ReferencedAssemblies 'System.Runtime.Loader','System.Runtime.InteropServices','System.Collections' } catch { Write-Verbose "Could not compile CoreRedirector: $_" } @@ -167,8 +306,59 @@ if ($PSVersionTable.PSEdition -ne "Core") { } } -# REMOVED win-sqlclient logic - SqlClient is now directly in lib -$sqlclient = [System.IO.Path]::Combine($script:libraryroot, "lib", "Microsoft.Data.SqlClient.dll") +function Get-DbatoolsSqlClientPath { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$LibraryRoot + ) + + $libPath = [System.IO.Path]::Combine($LibraryRoot, "lib") + if ($PSVersionTable.PSEdition -eq "Core") { + if ($IsWindows) { + $runtimeSqlClient = [System.IO.Path]::Combine($libPath, "runtimes", "win", "lib", "net8.0", "Microsoft.Data.SqlClient.dll") + } else { + $runtimeSqlClient = [System.IO.Path]::Combine($libPath, "runtimes", "unix", "lib", "net8.0", "Microsoft.Data.SqlClient.dll") + } + + if (Test-Path $runtimeSqlClient) { + return $runtimeSqlClient + } + } + + [System.IO.Path]::Combine($libPath, "Microsoft.Data.SqlClient.dll") +} + +function Add-DbatoolsNativeSearchPath { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$LibraryRoot + ) + + if ($PSVersionTable.PSEdition -ne "Core" -or -not $IsWindows) { + return + } + + $architecture = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant() + $nativeRid = "win-$architecture" + $nativePath = [System.IO.Path]::Combine($LibraryRoot, "lib", "runtimes", $nativeRid, "native") + if (-not (Test-Path $nativePath)) { + return + } + + $pathSeparator = [System.IO.Path]::PathSeparator + $pathParts = $env:PATH -split [Regex]::Escape([string]$pathSeparator) + if ($pathParts -contains $nativePath) { + return + } + + $env:PATH = $nativePath + $pathSeparator + $env:PATH + [System.Environment]::SetEnvironmentVariable("PATH", $env:PATH, "Process") +} + +Add-DbatoolsNativeSearchPath -LibraryRoot $script:libraryroot +$sqlclient = Get-DbatoolsSqlClientPath -LibraryRoot $script:libraryroot # Get loaded assemblies once for reuse (used for AvoidConflicts checks and later assembly loading) $script:loadedAssemblies = [System.AppDomain]::CurrentDomain.GetAssemblies() @@ -328,4 +518,4 @@ foreach ($name in $names) { if ($PSVersionTable.PSEdition -ne "Core" -and $redirector) { # Store the redirector in script scope so it stays alive and can be accessed if needed $script:assemblyRedirector = $redirector -} \ No newline at end of file +} From 0bb5e9dbe97730516e86dd1a1d396e04b589de88 Mon Sep 17 00:00:00 2001 From: Chrissy LeMaire Date: Sun, 3 May 2026 08:30:46 +0200 Subject: [PATCH 2/4] fix: address SqlClient runtime review follow-ups --- .github/workflows/build.yml | 15 +++++++++++---- dbatools.library.psm1 | 17 ++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eeb2c9cb..5ce5e702 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -131,14 +131,21 @@ jobs: } $location = $sqlClient.Location.Replace('/', '\') - $expected = "runtimes\win\lib\net8.0\Microsoft.Data.SqlClient.dll" + $expected = if ($IsWindows) { + "runtimes\win\lib\net8.0\Microsoft.Data.SqlClient.dll" + } else { + "runtimes\unix\lib\net8.0\Microsoft.Data.SqlClient.dll" + } + if ($location -notlike "*$expected") { throw "Microsoft.Data.SqlClient loaded from unexpected path: $($sqlClient.Location)" } - $sniPath = "./artifacts/dbatools.library/core/lib/runtimes/win-x64/native/Microsoft.Data.SqlClient.SNI.dll" - if (-not (Test-Path $sniPath)) { - throw "Missing Windows x64 SqlClient native SNI DLL at $sniPath" + if ($IsWindows) { + $sniPath = "./artifacts/dbatools.library/core/lib/runtimes/win-x64/native/Microsoft.Data.SqlClient.SNI.dll" + if (-not (Test-Path $sniPath)) { + throw "Missing Windows x64 SqlClient native SNI DLL at $sniPath" + } } Write-Host "SqlClient runtime asset verified: $($sqlClient.Location)" diff --git a/dbatools.library.psm1 b/dbatools.library.psm1 index e7c41634..0bfd5018 100644 --- a/dbatools.library.psm1 +++ b/dbatools.library.psm1 @@ -97,6 +97,7 @@ if ($PSVersionTable.PSEdition -ne "Core") { $coreSource = @" using System; using System.Collections.Generic; + using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.InteropServices; @@ -145,8 +146,9 @@ if ($PSVersionTable.PSEdition -ne "Core") { { return AssemblyLoadContext.Default.LoadFromAssemblyPath(dllPath); } - catch + catch (Exception ex) { + Trace.TraceWarning("Failed to load managed assembly from '{0}': {1}", dllPath, ex.Message); // Failed to load, try the next candidate } } @@ -172,8 +174,9 @@ if ($PSVersionTable.PSEdition -ne "Core") { { return NativeLibrary.Load(nativePath); } - catch + catch (Exception ex) { + Trace.TraceWarning("Failed to load native library from '{0}': {1}", nativePath, ex.Message); // Failed to load, try the next candidate } } @@ -189,20 +192,19 @@ if ($PSVersionTable.PSEdition -ne "Core") { string architectureRid = GetArchitectureRid(); var paths = new List(); - paths.Add(Path.Combine(_libPath, fileName)); if (!String.IsNullOrEmpty(platformRid)) { paths.Add(Path.Combine(_libPath, "runtimes", platformRid, "lib", "net8.0", fileName)); - paths.Add(Path.Combine(_libPath, "runtimes", platformRid, "lib", "netstandard1.6", fileName)); } if (!String.IsNullOrEmpty(architectureRid)) { paths.Add(Path.Combine(_libPath, "runtimes", architectureRid, "lib", "net8.0", fileName)); - paths.Add(Path.Combine(_libPath, "runtimes", architectureRid, "lib", "netstandard1.6", fileName)); } + paths.Add(Path.Combine(_libPath, fileName)); + return paths.ToArray(); } @@ -240,6 +242,8 @@ if ($PSVersionTable.PSEdition -ne "Core") { private static string GetPlatformRid() { + // Managed runtime assets used by the module, especially SqlClient, ship + // win/unix folders. OS-specific native assets are handled by GetArchitectureRid. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return "win"; @@ -293,7 +297,7 @@ if ($PSVersionTable.PSEdition -ne "Core") { "@ try { - $null = Add-Type -TypeDefinition $coreSource -ReferencedAssemblies 'System.Runtime.Loader','System.Runtime.InteropServices','System.Collections' + $null = Add-Type -TypeDefinition $coreSource -ReferencedAssemblies 'System.Runtime.Loader','System.Runtime.InteropServices','System.Collections','System.Diagnostics.TraceSource' } catch { Write-Verbose "Could not compile CoreRedirector: $_" } @@ -354,7 +358,6 @@ function Add-DbatoolsNativeSearchPath { } $env:PATH = $nativePath + $pathSeparator + $env:PATH - [System.Environment]::SetEnvironmentVariable("PATH", $env:PATH, "Process") } Add-DbatoolsNativeSearchPath -LibraryRoot $script:libraryroot From 1b62694277927d6efcbef4fb3a2a4b1acc5fafcf Mon Sep 17 00:00:00 2001 From: Chrissy LeMaire Date: Sun, 3 May 2026 08:52:47 +0200 Subject: [PATCH 3/4] Address SqlClient runtime loader review --- dbatools.library.psm1 | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/dbatools.library.psm1 b/dbatools.library.psm1 index 0bfd5018..2fb561c9 100644 --- a/dbatools.library.psm1 +++ b/dbatools.library.psm1 @@ -90,8 +90,7 @@ if ($PSVersionTable.PSEdition -ne "Core") { # PowerShell Core: Use AssemblyLoadContext.Resolving event for version redirection # This handles version mismatches when SqlServer module loads different versions of assemblies # IMPORTANT: Must be implemented in C# because the resolver runs on .NET threads without PowerShell runspaces - $dir = [System.IO.Path]::Combine($script:libraryroot, "lib") - $dir = ("$dir" + [System.IO.Path]::DirectorySeparatorChar).Replace('\', '\\') + $dir = [System.IO.Path]::Combine($script:libraryroot, "lib") + [System.IO.Path]::DirectorySeparatorChar if (-not ("CoreRedirector" -as [type])) { $coreSource = @" @@ -107,6 +106,8 @@ if ($PSVersionTable.PSEdition -ne "Core") { { private static string _libPath; private static bool _registered = false; + private static readonly string _platformRid = ComputePlatformRid(); + private static readonly string _architectureRid = ComputeArchitectureRid(); public static void Register(string libPath) { @@ -157,17 +158,17 @@ if ($PSVersionTable.PSEdition -ne "Core") { return null; } + // This resolver is global to the load context, but it only returns module-owned native assets from _libPath. private static IntPtr OnResolvingUnmanagedDll(Assembly assembly, string libraryName) { - string architectureRid = GetArchitectureRid(); - if (String.IsNullOrEmpty(architectureRid)) + if (String.IsNullOrEmpty(_architectureRid)) { return IntPtr.Zero; } foreach (string fileName in GetNativeLibraryNames(libraryName)) { - string nativePath = Path.Combine(_libPath, "runtimes", architectureRid, "native", fileName); + string nativePath = Path.Combine(_libPath, "runtimes", _architectureRid, "native", fileName); if (File.Exists(nativePath)) { try @@ -188,19 +189,17 @@ if ($PSVersionTable.PSEdition -ne "Core") { private static string[] GetManagedAssemblyPaths(string name) { string fileName = name + ".dll"; - string platformRid = GetPlatformRid(); - string architectureRid = GetArchitectureRid(); var paths = new List(); - if (!String.IsNullOrEmpty(platformRid)) + if (!String.IsNullOrEmpty(_platformRid)) { - paths.Add(Path.Combine(_libPath, "runtimes", platformRid, "lib", "net8.0", fileName)); + paths.Add(Path.Combine(_libPath, "runtimes", _platformRid, "lib", "net8.0", fileName)); } - if (!String.IsNullOrEmpty(architectureRid)) + if (!String.IsNullOrEmpty(_architectureRid)) { - paths.Add(Path.Combine(_libPath, "runtimes", architectureRid, "lib", "net8.0", fileName)); + paths.Add(Path.Combine(_libPath, "runtimes", _architectureRid, "lib", "net8.0", fileName)); } paths.Add(Path.Combine(_libPath, fileName)); @@ -240,10 +239,10 @@ if ($PSVersionTable.PSEdition -ne "Core") { return names.ToArray(); } - private static string GetPlatformRid() + private static string ComputePlatformRid() { // Managed runtime assets used by the module, especially SqlClient, ship - // win/unix folders. OS-specific native assets are handled by GetArchitectureRid. + // win/unix folders. OS-specific native assets are handled by ComputeArchitectureRid. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return "win"; @@ -252,7 +251,7 @@ if ($PSVersionTable.PSEdition -ne "Core") { return "unix"; } - private static string GetArchitectureRid() + private static string ComputeArchitectureRid() { string osPart; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) From e3f2512e0e5c113691f28f4a9dd4becfa2ec37aa Mon Sep 17 00:00:00 2001 From: Chrissy LeMaire Date: Sun, 3 May 2026 09:07:19 +0200 Subject: [PATCH 4/4] Extract SqlClient runtime verification action --- .github/actions/verify-sqlclient/action.yml | 48 +++++++++++++++++++++ .github/workflows/build-release.yml | 32 +------------- .github/workflows/build.yml | 32 +------------- 3 files changed, 50 insertions(+), 62 deletions(-) create mode 100644 .github/actions/verify-sqlclient/action.yml diff --git a/.github/actions/verify-sqlclient/action.yml b/.github/actions/verify-sqlclient/action.yml new file mode 100644 index 00000000..681ea1fa --- /dev/null +++ b/.github/actions/verify-sqlclient/action.yml @@ -0,0 +1,48 @@ +name: Verify SqlClient runtime asset +description: Verifies that Microsoft.Data.SqlClient loads from the platform-specific runtime asset. + +inputs: + module-path: + description: Path to the dbatools.library module manifest. + required: false + default: ./artifacts/dbatools.library/dbatools.library.psd1 + core-lib-path: + description: Path to the built core/lib directory. + required: false + default: ./artifacts/dbatools.library/core/lib + +runs: + using: composite + steps: + - name: Verify SqlClient runtime asset selection + shell: pwsh + run: | + Import-Module "${{ inputs.module-path }}" -Force + + $sqlClient = [System.AppDomain]::CurrentDomain.GetAssemblies() | + Where-Object { $_.GetName().Name -eq 'Microsoft.Data.SqlClient' } | + Select-Object -First 1 + + if (-not $sqlClient) { + throw "Microsoft.Data.SqlClient was not loaded" + } + + $location = $sqlClient.Location.Replace('/', '\') + $expected = if ($IsWindows) { + "runtimes\win\lib\net8.0\Microsoft.Data.SqlClient.dll" + } else { + "runtimes\unix\lib\net8.0\Microsoft.Data.SqlClient.dll" + } + + if ($location -notlike "*$expected") { + throw "Microsoft.Data.SqlClient loaded from unexpected path: $($sqlClient.Location)" + } + + if ($IsWindows) { + $sniPath = Join-Path "${{ inputs.core-lib-path }}" "runtimes/win-x64/native/Microsoft.Data.SqlClient.SNI.dll" + if (-not (Test-Path $sniPath)) { + throw "Missing Windows x64 SqlClient native SNI DLL at $sniPath" + } + } + + Write-Host "SqlClient runtime asset verified: $($sqlClient.Location)" diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index eff70cd6..6e1fea1b 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -202,37 +202,7 @@ jobs: } - name: Verify SqlClient runtime asset selection - shell: pwsh - run: | - Import-Module ./artifacts/dbatools.library/dbatools.library.psd1 -Force - - $sqlClient = [System.AppDomain]::CurrentDomain.GetAssemblies() | - Where-Object { $_.GetName().Name -eq 'Microsoft.Data.SqlClient' } | - Select-Object -First 1 - - if (-not $sqlClient) { - throw "Microsoft.Data.SqlClient was not loaded" - } - - $location = $sqlClient.Location.Replace('/', '\') - $expected = if ($IsWindows) { - "runtimes\win\lib\net8.0\Microsoft.Data.SqlClient.dll" - } else { - "runtimes\unix\lib\net8.0\Microsoft.Data.SqlClient.dll" - } - - if ($location -notlike "*$expected") { - throw "Microsoft.Data.SqlClient loaded from unexpected path: $($sqlClient.Location)" - } - - if ($IsWindows) { - $sniPath = "./artifacts/dbatools.library/core/lib/runtimes/win-x64/native/Microsoft.Data.SqlClient.SNI.dll" - if (-not (Test-Path $sniPath)) { - throw "Missing Windows x64 SqlClient native SNI DLL at $sniPath" - } - } - - Write-Host "SqlClient runtime asset verified: $($sqlClient.Location)" + uses: ./.github/actions/verify-sqlclient - name: Install SqlPackagetests shell: pwsh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ce5e702..e375ed83 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -118,37 +118,7 @@ jobs: Write-Host "Module path: $($module.Path)" - name: Verify SqlClient runtime asset selection - shell: pwsh - run: | - Import-Module ./artifacts/dbatools.library/dbatools.library.psd1 -Force - - $sqlClient = [System.AppDomain]::CurrentDomain.GetAssemblies() | - Where-Object { $_.GetName().Name -eq 'Microsoft.Data.SqlClient' } | - Select-Object -First 1 - - if (-not $sqlClient) { - throw "Microsoft.Data.SqlClient was not loaded" - } - - $location = $sqlClient.Location.Replace('/', '\') - $expected = if ($IsWindows) { - "runtimes\win\lib\net8.0\Microsoft.Data.SqlClient.dll" - } else { - "runtimes\unix\lib\net8.0\Microsoft.Data.SqlClient.dll" - } - - if ($location -notlike "*$expected") { - throw "Microsoft.Data.SqlClient loaded from unexpected path: $($sqlClient.Location)" - } - - if ($IsWindows) { - $sniPath = "./artifacts/dbatools.library/core/lib/runtimes/win-x64/native/Microsoft.Data.SqlClient.SNI.dll" - if (-not (Test-Path $sniPath)) { - throw "Missing Windows x64 SqlClient native SNI DLL at $sniPath" - } - } - - Write-Host "SqlClient runtime asset verified: $($sqlClient.Location)" + uses: ./.github/actions/verify-sqlclient - name: Test AvoidConflicts with SqlServer module (Windows PowerShell 5.1) shell: powershell