Modules/businessdev.ALbuild.Apps/Private/Resolve-BcAlToolVersion.ps1
|
function Resolve-BcAlToolVersion { <# .SYNOPSIS Selects the AL Tool (dotnet tool) version that can compile a given AL runtime. .DESCRIPTION The AL Tool package is versioned so that its MAJOR equals the AL runtime version it supports (17.x compiles runtime 17 = BC28; 18.x compiles runtime 18 = BC29). A compiler older than the app's declared runtime fails the compile outright with AL1043 ("The runtime version 'x' is not supported by the AL compiler"), so the compiler has to be chosen from the target runtime rather than left at "whatever latest stable happens to be". A BC major that has not shipped yet (NextMajor) only has PRERELEASE ('-beta') AL Tool versions on the feed, which is why 'Auto' falls back to a prerelease when the required major has no stable release: it makes NextMajor builds work today and silently returns to the stable compiler the moment that major goes GA - without a pipeline change. Versions are read from the NuGet flat-container index (a plain JSON GET, no NuGet client). .PARAMETER PackageId The dotnet tool package id. Default 'Microsoft.Dynamics.BusinessCentral.Development.Tools'. .PARAMETER RequiredMajor The AL runtime major that must be supported (= the AL Tool major). 0 = no constraint, which selects the newest version allowed by -Prerelease. .PARAMETER Prerelease Auto (default) - prefer a stable release for the required major, fall back to a prerelease only when that major has none. Always - take the newest version even if it is a prerelease. Never - stable only; returns nothing when the required major has no stable release. .PARAMETER IndexUrl Override the version index URL (defaults to the nuget.org flat-container index for -PackageId). .PARAMETER TimeoutSec HTTP timeout for the index query. .EXAMPLE Resolve-BcAlToolVersion -RequiredMajor 18 # -> 18.0.39.10160-beta while BC29 is prerelease; the newest stable 18.x once it ships. .OUTPUTS PSCustomObject: Version, IsPrerelease, RequiredMajor, Available - or $null when nothing matches. #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [string] $PackageId = 'Microsoft.Dynamics.BusinessCentral.Development.Tools', [int] $RequiredMajor = 0, [ValidateSet('Auto', 'Always', 'Never')] [string] $Prerelease = 'Auto', [string] $IndexUrl, [int] $TimeoutSec = 60 ) if (-not $IndexUrl) { # The flat-container ("package base address") index lists every published version, prerelease # included; the id must be lower-cased for that endpoint. $IndexUrl = "https://api.nuget.org/v3-flatcontainer/$($PackageId.ToLowerInvariant())/index.json" } # Windows PowerShell 5.1 still negotiates TLS 1.0 by default on some build agents, which nuget.org # refuses; opt into TLS 1.2 for this call (additive, so nothing already enabled is turned off). try { if ([Net.ServicePointManager]::SecurityProtocol -notmatch 'Tls12') { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } } catch { Write-ALbuildLog -Level Warning "Could not enable TLS 1.2 for the AL Tool version query: $($_.Exception.Message)" } $raw = @() try { $response = Invoke-RestMethod -Uri $IndexUrl -Method Get -TimeoutSec $TimeoutSec if ($response -and ($response.PSObject.Properties.Name -contains 'versions')) { $raw = @($response.versions) } } catch { throw "Could not query the available AL Tool versions from '$IndexUrl': $($_.Exception.Message)" } if ($raw.Count -eq 0) { throw "The AL Tool version index '$IndexUrl' returned no versions." } # Split '<numeric>-<label>' (e.g. '18.0.39.10160-beta'); a version whose numeric part does not parse # is skipped rather than failing the whole selection. $parsed = foreach ($entry in $raw) { if (-not $entry) { continue } $text = [string] $entry $parts = $text -split '-', 2 $numericText = $parts[0] $label = if ($parts.Count -gt 1) { $parts[1] } else { '' } $numeric = $null if (-not [version]::TryParse($numericText, [ref] $numeric)) { continue } [PSCustomObject]@{ Version = $text Numeric = $numeric IsPrerelease = [bool] $label } } $parsed = @($parsed) if ($parsed.Count -eq 0) { throw "None of the $($raw.Count) AL Tool version(s) from '$IndexUrl' could be parsed." } $candidates = @($parsed) if ($RequiredMajor -gt 0) { $candidates = @($parsed | Where-Object { $_.Numeric.Major -eq $RequiredMajor }) } # Newest first; a stable release outranks a prerelease carrying the same numeric version. $ordered = @($candidates | Sort-Object -Property @{ Expression = 'Numeric'; Descending = $true }, @{ Expression = 'IsPrerelease'; Descending = $false }) $stable = @($ordered | Where-Object { -not $_.IsPrerelease }) $selected = switch ($Prerelease) { 'Never' { if ($stable.Count -gt 0) { $stable[0] } else { $null } } 'Always' { if ($ordered.Count -gt 0) { $ordered[0] } else { $null } } default { if ($stable.Count -gt 0) { $stable[0] } elseif ($ordered.Count -gt 0) { $ordered[0] } else { $null } } } if (-not $selected) { return $null } if ($selected.IsPrerelease) { Write-ALbuildLog -Level Warning ("AL Tool $($selected.Version) is a PRERELEASE" + $(if ($RequiredMajor -gt 0) { " - AL runtime $RequiredMajor has no stable compiler on the feed yet (expected while that BC major is in preview)" } else { '' }) + '.') } return [PSCustomObject]@{ Version = $selected.Version IsPrerelease = $selected.IsPrerelease RequiredMajor = $RequiredMajor Available = $candidates.Count } } |