Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .github/workflows/test-avoidconflicts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ on:
paths:
- 'dbatools.library.psm1'
- 'dbatools.library.psd1'
- 'project/dbatools/dbatools.csproj'
- 'tests/test-az-compatibility.ps1'
- 'tests/test-avoidconflicts.ps1'
- '.github/workflows/test-avoidconflicts.yml'
pull_request:
branches: [main]
paths:
- 'dbatools.library.psm1'
- 'dbatools.library.psd1'
- 'project/dbatools/dbatools.csproj'
- 'tests/test-az-compatibility.ps1'
- 'tests/test-avoidconflicts.ps1'
- '.github/workflows/test-avoidconflicts.yml'
workflow_dispatch:
Expand Down Expand Up @@ -300,10 +304,11 @@ jobs:
with:
name: dbatools-library
path: artifacts/dbatools.library/
- name: Install SqlServer module (PowerShell Core)
- name: Install compatibility test modules
run: |
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module SqlServer -Force -AllowClobber -Scope CurrentUser
Install-Module Az.Accounts -RequiredVersion 5.5.1 -Force -AllowClobber -Scope CurrentUser
- name: Run test-avoidconflicts.ps1 test suite
run: |
Write-Host "Running comprehensive test suite..." -ForegroundColor Cyan
Expand All @@ -312,6 +317,13 @@ jobs:
Write-Host "Test suite failed!" -ForegroundColor Red
exit 1
}
- name: Test Az.Accounts compatibility
run: |
./tests/test-az-compatibility.ps1
if ($LASTEXITCODE -ne 0) {
Write-Host "Az compatibility test failed!" -ForegroundColor Red
exit 1
}
summary:
name: Test Summary
runs-on: ubuntu-latest
Expand Down Expand Up @@ -342,6 +354,7 @@ jobs:
echo "### Test Coverage" >> $GITHUB_STEP_SUMMARY
echo "- Default module import (no conflicts)" >> $GITHUB_STEP_SUMMARY
echo "- SqlServer + dbatools.library with AvoidConflicts" >> $GITHUB_STEP_SUMMARY
echo "- Az.Accounts 5.5.1 compatibility in both import orders" >> $GITHUB_STEP_SUMMARY
echo "- Assembly version mismatch resolution" >> $GITHUB_STEP_SUMMARY
echo "- ArgumentList syntax validation" >> $GITHUB_STEP_SUMMARY
echo "- Both PowerShell Core and Windows PowerShell 5.1" >> $GITHUB_STEP_SUMMARY
Expand Down
11 changes: 6 additions & 5 deletions project/dbatools/dbatools.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,17 @@
<!-- Shared packages for both frameworks -->
<ItemGroup>
<PackageReference Include="Microsoft.AnalysisServices" Version="19.113.7" />
<!-- SqlClient 7 no longer brings these dependencies transitively, but dbatools.library loads Azure.Identity directly. -->
<PackageReference Include="Azure.Identity" Version="1.18.0" />
<!-- Align the Azure dependency stack with Az.Accounts 5.5.1 to avoid PowerShell assembly conflicts. -->
<PackageReference Include="Azure.Core" Version="1.56.0" />
<PackageReference Include="Azure.Identity" Version="1.21.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="7.0.1" />
<PackageReference Include="Microsoft.Data.SqlClient.Extensions.Azure" Version="1.0.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.83.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.84.0" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.84.0" />
<PackageReference Include="Microsoft.SqlServer.DacFx" Version="170.3.93" />
<PackageReference Include="Microsoft.SqlServer.SqlManagementObjects" Version="181.19.0" />
<PackageReference Include="Microsoft.SqlServer.XEvent.XELite" Version="2024.2.5.1" />
<!-- Azure.Core 1.41.0 incorrectly declares System.ClientModel 1.0.0 as dependency but uses types from 1.1.0+ -->
<PackageReference Include="System.ClientModel" Version="1.10.0" />
<PackageReference Include="System.ClientModel" Version="1.12.0" />
</ItemGroup>

<!-- Framework-specific packages -->
Expand Down
173 changes: 173 additions & 0 deletions tests/test-az-compatibility.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#!/usr/bin/env pwsh

[CmdletBinding()]
param (
[string]$ModulePath = (Join-Path $PSScriptRoot '..\artifacts\dbatools.library\dbatools.library.psd1'),

[string]$AzAccountsVersion = '5.5.1'
)

$ErrorActionPreference = 'Stop'

$minimumVersions = [ordered]@{
'Azure.Core' = [Version]'1.56.0'
'Azure.Identity' = [Version]'1.21.0'
'System.ClientModel' = [Version]'1.12.0'
'Microsoft.Identity.Client' = [Version]'4.84.0'
'Microsoft.Identity.Client.Extensions.Msal' = [Version]'4.84.0'
}

function Get-ProductVersion {
param (
[Parameter(Mandatory)]
[string]$Path
)

$versionInfo = [Diagnostics.FileVersionInfo]::GetVersionInfo((Resolve-Path -LiteralPath $Path))
return [Version](($versionInfo.ProductVersion -split '\+')[0])
}

function Assert-PackagedDependencyVersions {
param (
[Parameter(Mandatory)]
[string]$ManifestPath
)

$moduleRoot = Split-Path -Parent (Resolve-Path -LiteralPath $ManifestPath)
foreach ($runtime in 'core', 'desktop') {
foreach ($dependency in $minimumVersions.GetEnumerator()) {
$assemblyPath = Join-Path $moduleRoot "$runtime\lib\$($dependency.Key).dll"
if (-not (Test-Path -LiteralPath $assemblyPath)) {
throw "Missing packaged dependency: $assemblyPath"
}

$actualVersion = Get-ProductVersion -Path $assemblyPath
if ($actualVersion -lt $dependency.Value) {
throw "$runtime/lib/$($dependency.Key).dll is $actualVersion; Az.Accounts $AzAccountsVersion compatibility requires $($dependency.Value) or newer."
}
}
}
}

function Invoke-ImportOrderTest {
param (
[Parameter(Mandatory)]
[string]$PowerShellExecutable,

[Parameter(Mandatory)]
[string]$HostName,

[Parameter(Mandatory)]
[ValidateSet('DbatoolsFirst', 'AzFirst')]
[string]$ImportOrder,

[Parameter(Mandatory)]
[string]$ManifestPath,

[Parameter(Mandatory)]
[string]$AzAccountsManifest,

[Parameter(Mandatory)]
[string]$DbatoolsMsalExtensionsPath,

[Parameter(Mandatory)]
[string]$AzMsalExtensionsPath
)

$result = & $PowerShellExecutable -NoProfile -Command {
param($DbatoolsManifest, $AzManifest, $DbatoolsMsalPath, $AzMsalPath, $RequiredAzVersion, $Order, $RequiredVersions)

$ErrorActionPreference = 'Stop'
try {
if ($Order -eq 'DbatoolsFirst') {
Import-Module $DbatoolsManifest -Force -ErrorAction Stop
Add-Type -Path $DbatoolsMsalPath
Import-Module $AzManifest -Force -ErrorAction Stop
} else {
Import-Module $AzManifest -Force -ErrorAction Stop
Add-Type -Path $AzMsalPath
Import-Module $DbatoolsManifest -Force -ErrorAction Stop
}

$azAccounts = Get-Module Az.Accounts
if (-not $azAccounts -or $azAccounts.Version -ne [Version]$RequiredAzVersion) {
throw "Az.Accounts $RequiredAzVersion was not loaded."
}
if (-not (Get-AzEnvironment -Name AzureCloud)) {
throw 'Az.Accounts commands are not usable.'
}
if (-not ([Microsoft.SqlServer.Management.Smo.Server] -as [type])) {
throw 'dbatools SMO types are not usable.'
}
foreach ($dependencyName in $RequiredVersions.Keys) {
$loaded = @([AppDomain]::CurrentDomain.GetAssemblies() |
Where-Object { $_.GetName().Name -eq $dependencyName })
if ($dependencyName -in @('Azure.Core', 'Microsoft.Identity.Client.Extensions.Msal') -and $loaded.Count -eq 0) {
throw "Expected $dependencyName to be loaded."
}
if ($loaded.Count -eq 0) {
continue
}

foreach ($loadedAssembly in $loaded) {
$versionInfo = [Diagnostics.FileVersionInfo]::GetVersionInfo($loadedAssembly.Location)
$actualVersion = [Version](($versionInfo.ProductVersion -split '\+')[0])
$requiredVersion = [Version]$RequiredVersions[$dependencyName]
if ($actualVersion -lt $requiredVersion) {
throw "$dependencyName $actualVersion was loaded; expected $requiredVersion or newer."
}
}
}

'PASS'
} catch {
"FAIL: $($_.Exception.Message)"
}
} -args $ManifestPath, $AzAccountsManifest, $DbatoolsMsalExtensionsPath, $AzMsalExtensionsPath, $AzAccountsVersion, $ImportOrder, $minimumVersions

if ($LASTEXITCODE -ne 0 -or $result -ne 'PASS') {
throw "$HostName $ImportOrder compatibility test failed: $result"
}
}

if (-not (Test-Path -LiteralPath $ModulePath)) {
throw "Packaged dbatools.library module not found: $ModulePath"
}

Assert-PackagedDependencyVersions -ManifestPath $ModulePath

$installedAzAccounts = Get-Module -ListAvailable Az.Accounts |
Where-Object { $_.Version -eq [Version]$AzAccountsVersion } |
Select-Object -First 1
if (-not $installedAzAccounts) {
throw "Az.Accounts $AzAccountsVersion is required for this compatibility test."
}

$moduleRoot = Split-Path -Parent (Resolve-Path -LiteralPath $ModulePath)
$azModuleRoot = Split-Path -Parent $installedAzAccounts.Path
$azMsalExtensions = Get-ChildItem -LiteralPath $azModuleRoot -Recurse -Filter 'Microsoft.Identity.Client.Extensions.Msal.dll' |
Select-Object -First 1
if (-not $azMsalExtensions) {
throw "Az.Accounts $AzAccountsVersion does not contain Microsoft.Identity.Client.Extensions.Msal.dll."
}

$powerShellHosts = [ordered]@{
'PowerShell Core' = [pscustomobject]@{
Executable = (Get-Command pwsh -ErrorAction Stop).Source
Runtime = 'core'
}
}
if ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::Windows)) {
$powerShellHosts['Windows PowerShell'] = [pscustomobject]@{
Executable = (Get-Command powershell.exe -ErrorAction Stop).Source
Runtime = 'desktop'
}
}

foreach ($powerShellHost in $powerShellHosts.GetEnumerator()) {
$dbatoolsMsalExtensions = Join-Path $moduleRoot "$($powerShellHost.Value.Runtime)\lib\Microsoft.Identity.Client.Extensions.Msal.dll"
Invoke-ImportOrderTest -PowerShellExecutable $powerShellHost.Value.Executable -HostName $powerShellHost.Key -ImportOrder DbatoolsFirst -ManifestPath $ModulePath -AzAccountsManifest $installedAzAccounts.Path -DbatoolsMsalExtensionsPath $dbatoolsMsalExtensions -AzMsalExtensionsPath $azMsalExtensions.FullName
Invoke-ImportOrderTest -PowerShellExecutable $powerShellHost.Value.Executable -HostName $powerShellHost.Key -ImportOrder AzFirst -ManifestPath $ModulePath -AzAccountsManifest $installedAzAccounts.Path -DbatoolsMsalExtensionsPath $dbatoolsMsalExtensions -AzMsalExtensionsPath $azMsalExtensions.FullName
}

Write-Host "Az.Accounts $AzAccountsVersion compatibility tests passed in both import orders on $($powerShellHosts.Keys -join ' and ')."
Loading