diff --git a/.pipelines/templates/release-validate-packagenames.yml b/.pipelines/templates/release-validate-packagenames.yml index 5953366ff..acf250d81 100644 --- a/.pipelines/templates/release-validate-packagenames.yml +++ b/.pipelines/templates/release-validate-packagenames.yml @@ -56,7 +56,7 @@ jobs: - pwsh: | $message = @() Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.rpm | ForEach-Object { - if($_.Name -notmatch 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1.(rh|cm).(x86_64|aarch64)\.rpm') + if($_.Name -notmatch '^powershell-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(preview\.\d+\.)?(rh|cm|azl4)\.(x86_64|aarch64)\.rpm$') { $messageInstance = "$($_.Name) is not a valid package name" $message += $messageInstance diff --git a/test/packaging/linux/package-validation.tests.ps1 b/test/packaging/linux/package-validation.tests.ps1 index 3b961120f..8607b3934 100644 --- a/test/packaging/linux/package-validation.tests.ps1 +++ b/test/packaging/linux/package-validation.tests.ps1 @@ -15,9 +15,37 @@ Describe "Linux Package Name Validation" { } Write-Verbose "Artifacts directory: $artifactsDir" -Verbose + + # Keep this pattern aligned with the release validation pipeline. + $rpmPackageNamePattern = '^powershell-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(preview\.\d+\.)?(rh|cm|azl4)\.(x86_64|aarch64)\.rpm$' } Context "RPM Package Names" { + It "Should accept the Azure Linux 4 package name" -TestCases @( + @{ + Architecture = "x86_64" + Name = "powershell-preview-7.7.0_preview.5-1.azl4.x86_64.rpm" + } + @{ + Architecture = "aarch64" + Name = "powershell-preview-7.7.0_preview.5-1.azl4.aarch64.rpm" + } + ) { + param($Name) + + $Name | Should -Match $rpmPackageNamePattern + } + + It "Should reject malformed RPM package name " -TestCases @( + @{ Name = "prefix-powershell-preview-7.7.0_preview.5-1.azl4.x86_64.rpm" } + @{ Name = "powershell-preview-7.7.0_preview.5-1Xazl4.x86_64.rpm" } + @{ Name = "powershell-preview-7.7.0_preview.5-1.azl4.x86_64.rpm.backup" } + ) { + param($Name) + + $Name | Should -Not -Match $rpmPackageNamePattern + } + It "Should have valid RPM package names" { $rpmPackages = Get-ChildItem -Path $artifactsDir -Recurse -Filter *.rpm -ErrorAction SilentlyContinue @@ -32,9 +60,8 @@ Describe "Linux Package Name Validation" { # (_[a-z]*\.\d+)? : Optional underscore, letters, dot, and digits (e.g., _alpha.1) # -1\. : Literal '-1.' # (preview\.\d+\.)? : Optional 'preview.' and digits, followed by a dot - # (rh|cm)\. : Either 'rh.' or 'cm.' + # (rh|cm|azl4)\. : RPM distribution suffix # (x86_64|aarch64)\.rpm$ : Architecture and file extension - $rpmPackageNamePattern = 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(preview\.\d+\.)?(rh|cm)\.(x86_64|aarch64)\.rpm' foreach ($package in $rpmPackages) { if ($package.Name -notmatch $rpmPackageNamePattern) { diff --git a/test/packaging/packaging.tests.ps1 b/test/packaging/packaging.tests.ps1 index a7d322205..7c872095a 100644 --- a/test/packaging/packaging.tests.ps1 +++ b/test/packaging/packaging.tests.ps1 @@ -24,6 +24,159 @@ Describe "Packaging Module Functions" { } } + Context "Azure Linux RPM packaging" { + BeforeAll { + $afterInstallScript = Join-Path $TestDrive "after-install.sh" + $afterRemoveScript = Join-Path $TestDrive "after-remove.sh" + "echo installed" | Set-Content -Path $afterInstallScript + "echo removed" | Set-Content -Path $afterRemoveScript + $linkInfo = & (Get-Module packaging) { + [LinkInfo] @{ + Source = "/tmp/pwsh" + Destination = "/usr/bin/pwsh" + } + } + } + + It "Should target Azure Linux 3 and 4 for framework-dependent RPMs" { + $distributions = & (Get-Module packaging) { $Script:RedhatFddDistributions } + $distributions | Should -Be @("cm", "azl4") + } + + It "Should generate an Azure Linux 4 RPM file name for " -TestCases @( + @{ + HostArchitecture = "x86_64" + Expected = "powershell-preview-7.7.0_preview.5-1.azl4.x86_64.rpm" + } + @{ + HostArchitecture = "aarch64" + Expected = "powershell-preview-7.7.0_preview.5-1.azl4.aarch64.rpm" + } + ) { + param($HostArchitecture, $Expected) + + Get-RpmPackageFileName ` + -Name "powershell-preview" ` + -Version "7.7.0-preview.5" ` + -Iteration "1" ` + -Distribution "azl4" ` + -HostArchitecture $HostArchitecture | + Should -BeExactly $Expected + } + + It "Should write the Azure Linux 4 RPM Release metadata for " -TestCases @( + @{ HostArchitecture = "x86_64" } + @{ HostArchitecture = "aarch64" } + ) { + param($HostArchitecture) + + $spec = New-RpmSpec ` + -Name "powershell-preview" ` + -Version "7.7.0-preview.5" ` + -Iteration "1" ` + -Description "PowerShell test package" ` + -Staging "/tmp/staging" ` + -Destination "/opt/microsoft/powershell/7-preview" ` + -ManGzipFile "/tmp/pwsh.1.gz" ` + -ManDestination "/usr/share/man/man1/pwsh-preview.1.gz" ` + -LinkInfo $linkInfo ` + -Dependencies @("glibc") ` + -AfterInstallScript $afterInstallScript ` + -AfterRemoveScript $afterRemoveScript ` + -Distribution "azl4" ` + -HostArchitecture $HostArchitecture + + $spec | Should -Match "(?m)^Release:\s+1\.azl4\r?$" + $spec | Should -Match "(?m)^\* .+ - 7\.7\.0_preview\.5-1\.azl4\r?$" + } + + It "Should select the current master dependencies for with runtime " -TestCases @( + @{ Distribution = "cm"; Runtime = "linux-x64"; IncludeDotnetRuntime = $false } + @{ Distribution = "azl4"; Runtime = "linux-arm64"; IncludeDotnetRuntime = $false } + @{ Distribution = "cm"; Runtime = "fxdependent-linux-x64"; IncludeDotnetRuntime = $true } + @{ Distribution = "azl4"; Runtime = "fxdependent-linux-arm64"; IncludeDotnetRuntime = $true } + ) { + param($Distribution, $Runtime, $IncludeDotnetRuntime) + + $dependencies = & (Get-Module packaging) { + param($Distribution, $Runtime) + + $previousOptions = $Script:Options + try { + $Script:Options = [pscustomobject] @{ Runtime = $Runtime } + @(Get-PackageDependencies -Distribution $Distribution) + } + finally { + $Script:Options = $previousOptions + } + } $Distribution $Runtime + + $expectedDependencies = @( + "ca-certificates" + "glibc" + "icu" + "libgcc" + "libstdc++" + "openssl-libs" + "tzdata" + ) + if ($IncludeDotnetRuntime) { + $expectedDependencies += "dotnet-runtime-11.0" + } + + $dependencies | Should -BeExactly $expectedDependencies + } + } + + Context "Azure Linux PMC mappings" { + BeforeAll { + $mappingPath = Join-Path $PSScriptRoot "../../tools/packages.microsoft.com/mapping.json" + $mapping = Get-Content -Path $mappingPath -Raw | ConvertFrom-Json + } + + It "Should map packages to " -TestCases @( + @{ + Channel = "preview" + Architecture = "x86_64" + Url = "azurelinux-4-preview-microsoft-x86_64" + } + @{ + Channel = "preview" + Architecture = "aarch64" + Url = "azurelinux-4-preview-microsoft-aarch64" + } + @{ + Channel = "stable" + Architecture = "x86_64" + Url = "azurelinux-4-prod-microsoft-x86_64" + } + @{ + Channel = "stable" + Architecture = "aarch64" + Url = "azurelinux-4-prod-microsoft-aarch64" + } + ) { + param($Channel, $Architecture, $Url) + + $mappedPackage = @($mapping.Packages | Where-Object url -EQ $Url) + $mappedPackage.Count | Should -Be 1 + $mappedPackage[0].channel | Should -BeExactly $Channel + $mappedPackage[0].distribution | Should -BeExactly @("azl") + $mappedPackage[0].PackageFormat | + Should -BeExactly "PACKAGE_NAME-POWERSHELL_RELEASE-1.azl4.$Architecture.rpm" + } + + It "Should remove the Azure Linux 4 beta mappings" { + @($mapping.Packages | Where-Object url -Like "azurelinux-4.0-beta-*").Count | Should -Be 0 + } + + It "Should retain all Azure Linux 3 .cm mappings" { + $azureLinux3Mappings = @($mapping.Packages | Where-Object url -Like "azurelinux-3.0-*") + $azureLinux3Mappings.Count | Should -Be 4 + $azureLinux3Mappings.PackageFormat | Should -Match "\.cm\.(x86_64|aarch64)\.rpm$" + } + } + Context "Get-MacOSPackageIdentifierInfo function (New-MacOSPackage logic)" { It "Should detect preview builds and return preview identifier" { $result = Get-MacOSPackageIdentifierInfo -Version "7.6.0-preview.6" -LTS:$false diff --git a/tools/packages.microsoft.com/mapping.json b/tools/packages.microsoft.com/mapping.json index 1e6184bff..45d511a87 100644 --- a/tools/packages.microsoft.com/mapping.json +++ b/tools/packages.microsoft.com/mapping.json @@ -61,18 +61,36 @@ "channel": "preview" }, { - "url": "azurelinux-4.0-beta-microsoft-aarch64", + "url": "azurelinux-4-preview-microsoft-x86_64", "distribution": [ "azl" ], - "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.cm.aarch64.rpm" + "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.azl4.x86_64.rpm", + "channel": "preview" }, { - "url": "azurelinux-4.0-beta-microsoft-x86_64", + "url": "azurelinux-4-preview-microsoft-aarch64", "distribution": [ "azl" ], - "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.cm.x86_64.rpm" + "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.azl4.aarch64.rpm", + "channel": "preview" + }, + { + "url": "azurelinux-4-prod-microsoft-x86_64", + "distribution": [ + "azl" + ], + "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.azl4.x86_64.rpm", + "channel": "stable" + }, + { + "url": "azurelinux-4-prod-microsoft-aarch64", + "distribution": [ + "azl" + ], + "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.azl4.aarch64.rpm", + "channel": "stable" }, { "url": "microsoft-debian-stretch-prod", diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 775558711..b5eb1166e 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -10,7 +10,7 @@ $packagingStrings = Import-PowerShellDataFile "$PSScriptRoot\packaging.strings.p Import-Module "$PSScriptRoot\..\Xml" -ErrorAction Stop -Force $DebianDistributions = @("deb") $RedhatFullDistributions = @("rh") -$RedhatFddDistributions = @("cm") +$RedhatFddDistributions = @("cm", "azl4") $RedhatDistributions = @() $RedhatDistributions += $RedhatFullDistributions $RedhatDistributions += $RedhatFddDistributions @@ -1237,10 +1237,15 @@ function New-UnixPackage { throw "rpmbuild failed with exit code $exitCode" } - # Find the generated RPM - $rpmFile = Get-ChildItem -Path (Join-Path $rpmsDir $HostArchitecture) -Filter "*.rpm" -ErrorAction Stop | - Sort-Object -Property LastWriteTime -Descending | - Select-Object -First 1 + # Find the exact RPM produced from the spec metadata. + $rpmFileName = Get-RpmPackageFileName ` + -Name $Name ` + -Version $packageVersion ` + -Iteration $Iteration ` + -Distribution $DebDistro ` + -HostArchitecture $HostArchitecture + $rpmArchitecturePath = Join-Path $rpmsDir $HostArchitecture + $rpmFile = Get-Item -Path (Join-Path $rpmArchitecturePath $rpmFileName) -ErrorAction Stop if ($rpmFile) { # Copy RPM to current location @@ -1516,6 +1521,34 @@ Class LinkInfo [string] $Destination } +function Get-RpmPackageFileName +{ + <# + .SYNOPSIS + Gets the RPM file name produced by rpmbuild for the supplied package metadata. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string] $Name, + + [Parameter(Mandatory)] + [string] $Version, + + [Parameter(Mandatory)] + [string] $Iteration, + + [Parameter(Mandatory)] + [string] $Distribution, + + [Parameter(Mandatory)] + [string] $HostArchitecture + ) + + $rpmVersion = $Version -replace '-', '_' + return "$Name-$rpmVersion-$Iteration.$Distribution.$HostArchitecture.rpm" +} + function New-RpmSpec { param( @@ -1563,7 +1596,7 @@ function New-RpmSpec # e.g., "7.6.0-preview.6" becomes Version: 7.6.0_preview.6 $rpmVersion = $Version -replace '-', '_' - # Build Release field with distribution suffix (e.g., "1.cm" or "1.rh") + # Build Release field with distribution suffix (e.g., "1.cm", "1.azl4", or "1.rh") # Don't use RPM macros - build the full release string in PowerShell $rpmRelease = "$Iteration.$Distribution" @@ -2105,7 +2138,7 @@ function Get-PackageDependencies "openssl-libs", "libicu" ) - } elseif ($Distribution -eq 'cm') { + } elseif ($Distribution -in 'cm', 'azl4') { # Azure Linux 3 and 4 use the same .NET 11 runtime dependencies: # https://github.com/dotnet/dotnet-docker/blob/7962e8c9336303584720344bd644287a1fe77844/src/runtime-deps/11.0/azurelinux4.0/amd64/Dockerfile $Dependencies = @(