[release/v7.5] Improve ADO package build and validation across platforms (#26814)

This commit is contained in:
Dongbo Wang
2026-02-13 14:53:09 -08:00
committed by GitHub
parent 458071221e
commit 189d64bf8c
5 changed files with 116 additions and 74 deletions
+1 -1
View File
@@ -204,7 +204,7 @@ jobs:
}
Import-Module Pester
$pesterConfig = New-PesterConfiguration
$pesterConfig.Run.Path = './tools/packaging/releaseTests/macOSPackage.tests.ps1'
$pesterConfig.Run.Path = './test/packaging/macos/package-validation.tests.ps1'
$pesterConfig.Run.PassThru = $true
$pesterConfig.Output.Verbosity = 'Detailed'
$pesterConfig.TestResult.Enabled = $true
+19 -7
View File
@@ -116,13 +116,25 @@ jobs:
Start-PSPackage -Type osxpkg -SkipReleaseChecks -MacOSRuntime $macosRuntime -ReleaseTag $(ReleaseTagVar) -PackageBinPath $signedFilesPath -LTS:$LTS
$pkgNameFilter = "powershell-*$macosRuntime.pkg"
$pkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $pkgNameFilter -Recurse -File | Select-Object -ExpandProperty FullName
Write-Host "##vso[artifact.upload containerfolder=macos-pkgs;artifactname=macos-pkgs]$pkgPath"
Write-Verbose -Verbose "Looking for pkg packages with filter: $pkgNameFilter in '$(Pipeline.Workspace)' to upload..."
$pkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $pkgNameFilter -Recurse -File
foreach($p in $pkgPath) {
$file = $p.FullName
Write-Verbose -verbose "Uploading $file to macos-pkgs"
Write-Host "##vso[artifact.upload containerfolder=macos-pkgs;artifactname=macos-pkgs]$file"
}
Start-PSPackage -Type tar -SkipReleaseChecks -MacOSRuntime $macosRuntime -ReleaseTag $(ReleaseTagVar) -PackageBinPath $signedFilesPath -LTS:$LTS
$tarPkgNameFilter = "powershell-*$macosRuntime.tar.gz"
$tarPkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $tarPkgNameFilter -Recurse -File | Select-Object -ExpandProperty FullName
Write-Host "##vso[artifact.upload containerfolder=macos-pkgs;artifactname=macos-pkgs]$tarPkgPath"
Write-Verbose -Verbose "Looking for tar packages with filter: $tarPkgNameFilter in '$(Pipeline.Workspace)' to upload..."
$tarPkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $tarPkgNameFilter -Recurse -File
foreach($t in $tarPkgPath) {
$file = $t.FullName
Write-Verbose -verbose "Uploading $file to macos-pkgs"
Write-Host "##vso[artifact.upload containerfolder=macos-pkgs;artifactname=macos-pkgs]$file"
}
displayName: 'Package ${{ parameters.buildArchitecture}}'
env:
@@ -195,14 +207,14 @@ jobs:
- pwsh: |
$signedPkg = Get-ChildItem -Path $(Pipeline.Workspace) -Filter "*osx*.zip" -File
$signedPkg | ForEach-Object {
Write-Verbose -Verbose "Signed package zip: $_"
if (-not (Test-Path $_)) {
throw "Package not found: $_"
}
if (-not (Test-Path $(ob_outputDirectory))) {
$null = New-Item -Path $(ob_outputDirectory) -ItemType Directory
}
+8 -3
View File
@@ -2399,7 +2399,7 @@ function Start-PSBootstrap {
Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo $PackageManager install -y rpm-build")) -IgnoreExitcode
}
}
# For Debian-based systems and Mariner, ensure dpkg-deb is available
if ($environment.IsLinux -and ($environment.IsDebianFamily -or $environment.IsMariner)) {
Write-Verbose -Verbose "Checking for dpkg-deb..."
@@ -2407,9 +2407,14 @@ function Start-PSBootstrap {
Write-Warning "dpkg-deb not found. Installing dpkg package..."
if ($environment.IsMariner) {
# For Mariner (Azure Linux), install the extended repo first to access dpkg.
Write-Verbose -verbose "BEGIN: /etc/os-release content:"
Get-Content /etc/os-release | Write-Verbose -verbose
Write-Verbose -verbose "END: /etc/os-release content"
Write-Verbose -Verbose "Installing azurelinux-repos-extended for Mariner..."
Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo $PackageManager install -y azurelinux-repos-extended")) -IgnoreExitcode
Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo $PackageManager install -y dpkg")) -IgnoreExitcode
Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo $PackageManager azurelinux-repos-extended")) -IgnoreExitcode -Verbose
Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo $PackageManager dpkg")) -IgnoreExitcode -Verbose
} else {
Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo apt-get install -y dpkg")) -IgnoreExitcode
}
@@ -1,51 +1,54 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
Describe "Verify macOS Package" {
BeforeAll {
Write-Verbose "In Describe BeforeAll" -Verbose
Import-Module $PSScriptRoot/../../../build.psm1
# Find the macOS package
$packagePath = $env:PACKAGE_FOLDER
if (-not $packagePath) {
$packagePath = Get-Location
}
Write-Verbose "Looking for package in: $packagePath" -Verbose
$package = Get-ChildItem -Path $packagePath -Filter "*.pkg" -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $package) {
Write-Warning "No .pkg file found in $packagePath"
} else {
Write-Verbose "Found package: $($package.FullName)" -Verbose
}
# Set up test directories
$script:package = $package
$script:expandDir = $null
$script:payloadDir = $null
$script:extractedFiles = @()
if ($package) {
# Use TestDrive for temporary directories - pkgutil will create the expand directory
$script:expandDir = Join-Path "TestDrive:" -ChildPath "package-contents-test"
$expandDirResolved = (Resolve-Path "TestDrive:").ProviderPath
$script:expandDir = Join-Path $expandDirResolved -ChildPath "package-contents-test"
Write-Verbose "Expanding package to: $($script:expandDir)" -Verbose
# pkgutil will create the directory itself, so don't pre-create it
Start-NativeExecution {
pkgutil --expand $package.FullName $script:expandDir
}
# Extract the payload to verify files
$script:payloadDir = Join-Path "TestDrive:" -ChildPath "package-payload-test"
$payloadDirResolved = (Resolve-Path "TestDrive:").ProviderPath
$script:payloadDir = Join-Path $payloadDirResolved -ChildPath "package-payload-test"
# Create payload directory since cpio needs it
if (-not (Test-Path $script:payloadDir)) {
$null = New-Item -ItemType Directory -Path $script:payloadDir -Force
}
$componentPkg = Get-ChildItem -Path $script:expandDir -Filter "*.pkg" -Recurse | Select-Object -First 1
if ($componentPkg) {
Write-Verbose "Extracting payload from: $($componentPkg.FullName)" -Verbose
@@ -57,40 +60,60 @@ Describe "Verify macOS Package" {
Pop-Location
}
}
# Get all extracted files for verification
$script:extractedFiles = Get-ChildItem -Path $script:payloadDir -Recurse -ErrorAction SilentlyContinue
Write-Verbose "Extracted $($script:extractedFiles.Count) files" -Verbose
}
}
AfterAll {
# TestDrive automatically cleans up, but we can ensure cleanup happens
# No manual cleanup needed as TestDrive handles it
}
Context "Package existence and structure" {
It "Package file should exist" {
$script:package | Should -Not -BeNullOrEmpty -Because "A .pkg file should be created"
$script:package.Extension | Should -Be ".pkg"
}
It "Package name should follow correct naming convention" {
$script:package | Should -Not -BeNullOrEmpty
# Regex pattern for valid macOS PKG package names.
# Valid examples:
# - powershell-7.4.13-osx-x64.pkg (Intel x64 - note: x64 with hyphens for compatibility)
# - powershell-7.4.13-osx-arm64.pkg (Apple Silicon)
# - powershell-preview-7.6.0-preview.6-osx-x64.pkg
# - powershell-lts-7.4.13-osx-arm64.pkg
$pkgPackageNamePattern = '^powershell(-preview|-lts)?-\d+\.\d+\.\d+(-[a-z]+\.\d+)?-osx-(x64|arm64)\.pkg$'
$script:package.Name | Should -Match $pkgPackageNamePattern -Because "Package name should follow the standard naming convention"
}
It "Package name should NOT use x86_64 with underscores" {
$script:package | Should -Not -BeNullOrEmpty
$script:package.Name | Should -Not -Match 'x86_64' -Because "Package should use 'x64' not 'x86_64' (with underscores) for compatibility"
}
It "Package should expand successfully" {
$script:expandDir | Should -Exist
Get-ChildItem -Path $script:expandDir | Should -Not -BeNullOrEmpty
}
It "Package should have a component package" {
$componentPkg = Get-ChildItem -Path $script:expandDir -Filter "*.pkg" -Recurse -ErrorAction SilentlyContinue
$componentPkg | Should -Not -BeNullOrEmpty -Because "Package should contain a component.pkg"
}
It "Payload should extract successfully" {
$script:payloadDir | Should -Exist
$script:extractedFiles | Should -Not -BeNullOrEmpty -Because "Package payload should contain files"
}
}
Context "Required files in package" {
BeforeAll {
$expectedFilePatterns = @{
@@ -99,7 +122,7 @@ Describe "Verify macOS Package" {
"Man page" = "usr/local/share/man/man1/pwsh*.gz"
"Launcher application plist" = "Applications/PowerShell*.app/Contents/Info.plist"
}
$testCases = @()
foreach ($key in $expectedFilePatterns.Keys) {
$testCases += @{
@@ -107,23 +130,23 @@ Describe "Verify macOS Package" {
Pattern = $expectedFilePatterns[$key]
}
}
$script:testCases = $testCases
}
It "Should contain <Description>" -TestCases $script:testCases {
param($Description, $Pattern)
$found = $script:extractedFiles | Where-Object { $_.FullName -like "*$Pattern*" }
$found | Should -Not -BeNullOrEmpty -Because "$Description should exist in the package at path matching '$Pattern'"
}
}
Context "PowerShell binary verification" {
It "PowerShell executable should be executable" {
$pwshBinary = $script:extractedFiles | Where-Object { $_.FullName -like "*/pwsh" -and $_.FullName -like "*/microsoft/powershell/*" }
$pwshBinary | Should -Not -BeNullOrEmpty
# Check if file has executable permissions (on Unix-like systems)
if ($IsLinux -or $IsMacOS) {
$permissions = (Get-Item $pwshBinary[0].FullName).UnixFileMode
@@ -132,27 +155,27 @@ Describe "Verify macOS Package" {
}
}
}
Context "Launcher application" {
It "Launcher app should have proper bundle structure" {
$plistFile = $script:extractedFiles | Where-Object { $_.FullName -like "*PowerShell*.app/Contents/Info.plist" }
$plistFile | Should -Not -BeNullOrEmpty
# Verify the bundle has required components
$appPath = Split-Path (Split-Path $plistFile[0].FullName -Parent) -Parent
$macOSDir = Join-Path $appPath "Contents/MacOS"
$resourcesDir = Join-Path $appPath "Contents/Resources"
Test-Path $macOSDir | Should -Be $true -Because "App bundle should have Contents/MacOS directory"
Test-Path $resourcesDir | Should -Be $true -Because "App bundle should have Contents/Resources directory"
}
It "Launcher script should exist and be executable" {
$launcherScript = $script:extractedFiles | Where-Object {
$_.FullName -like "*PowerShell*.app/Contents/MacOS/PowerShell.sh"
$launcherScript = $script:extractedFiles | Where-Object {
$_.FullName -like "*PowerShell*.app/Contents/MacOS/PowerShell.sh"
}
$launcherScript | Should -Not -BeNullOrEmpty -Because "Launcher script should exist"
if ($IsLinux -or $IsMacOS) {
$permissions = (Get-Item $launcherScript[0].FullName).UnixFileMode
$permissions.ToString() | Should -Match 'x' -Because "Launcher script should have execute permissions"
+36 -34
View File
@@ -1239,15 +1239,15 @@ function New-UnixPackage {
# Use rpmbuild directly for RPM packages
if ($PSCmdlet.ShouldProcess("Create RPM package with rpmbuild")) {
Write-Log "Creating RPM package with rpmbuild..."
# Create rpmbuild directory structure
$rpmBuildRoot = Join-Path $env:HOME "rpmbuild"
$specsDir = Join-Path $rpmBuildRoot "SPECS"
$rpmsDir = Join-Path $rpmBuildRoot "RPMS"
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
New-Item -ItemType Directory -Path $rpmsDir -Force | Out-Null
# Generate RPM spec file
$specContent = New-RpmSpec `
-Name $Name `
@@ -1264,11 +1264,11 @@ function New-UnixPackage {
-LinkInfo $Links `
-Distribution $DebDistro `
-HostArchitecture $HostArchitecture
$specFile = Join-Path $specsDir "$Name.spec"
$specContent | Out-File -FilePath $specFile -Encoding ascii
Write-Verbose "Generated spec file: $specFile" -Verbose
# Log the spec file content
if ($env:GITHUB_ACTIONS -eq 'true') {
Write-Host "::group::RPM Spec File Content"
@@ -1277,7 +1277,7 @@ function New-UnixPackage {
} else {
Write-Verbose "RPM Spec File Content:`n$specContent" -Verbose
}
# Build RPM package
try {
# Use bash to properly handle rpmbuild arguments
@@ -1290,16 +1290,16 @@ function New-UnixPackage {
Write-Verbose "Running: $buildCmd" -Verbose
$Output = bash -c $buildCmd 2>&1
$exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
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 |
$rpmFile = Get-ChildItem -Path (Join-Path $rpmsDir $HostArchitecture) -Filter "*.rpm" -ErrorAction Stop |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -First 1
if ($rpmFile) {
# Copy RPM to current location
Copy-Item -Path $rpmFile.FullName -Destination $CurrentLocation -Force
@@ -1337,7 +1337,7 @@ function New-UnixPackage {
-AfterRemoveScript $AfterScriptInfo.AfterRemoveScript `
-HostArchitecture $HostArchitecture `
-CurrentLocation $CurrentLocation
$Output = @("Created package {:path=>""$($result.PackageName)""}")
}
catch {
@@ -1348,7 +1348,7 @@ function New-UnixPackage {
# Use native macOS packaging tools
if ($PSCmdlet.ShouldProcess("Create macOS package with pkgbuild/productbuild")) {
Write-Log "Creating macOS package with native tools..."
$macPkgArgs = @{
Name = $Name
Version = $packageVersion
@@ -1363,7 +1363,7 @@ function New-UnixPackage {
HostArchitecture = $HostArchitecture
CurrentLocation = $CurrentLocation
}
try {
$packageFile = New-MacOSPackage @macPkgArgs
$Output = @("Created package {:path=>""$($packageFile.Name)""}")
@@ -1386,7 +1386,7 @@ function New-UnixPackage {
Clear-MacOSLauncher
}
}
# Clean up rpmbuild directory if it was created
if ($Type -eq 'rpm') {
$rpmBuildRoot = Join-Path $env:HOME "rpmbuild"
@@ -1395,7 +1395,7 @@ function New-UnixPackage {
Remove-Item -Path $rpmBuildRoot -Recurse -Force -ErrorAction SilentlyContinue
}
}
if ($AfterScriptInfo.AfterInstallScript) {
Remove-Item -ErrorAction 'silentlycontinue' $AfterScriptInfo.AfterInstallScript -Force
}
@@ -1489,13 +1489,13 @@ function New-MacOsDistributionPackage
$resourcesDir = Join-Path -Path $tempDir -ChildPath 'resources'
New-Item -ItemType Directory -Path $resourcesDir -Force > $null
# Copy background file to temp directory
$backgroundFile = "$RepoRoot/assets/macDialog.png"
if (Test-Path $backgroundFile) {
Copy-Item -Path $backgroundFile -Destination $resourcesDir -Force
}
# Copy the component package to temp directory
$componentFileName = Split-Path -Leaf -Path $ComponentPackage
$tempComponentPath = Join-Path -Path $tempDir -ChildPath $componentFileName
@@ -1511,7 +1511,7 @@ function New-MacOsDistributionPackage
# Minimum OS version
$minOSVersion = "11.0" # macOS Big Sur minimum
# format distribution template with:
# 0 - title
# 1 - version
@@ -1522,8 +1522,10 @@ function New-MacOsDistributionPackage
$PackagingStrings.OsxDistributionTemplate -f $PackageName, $Version, $componentFileName, $minOSVersion, $PackageIdentifier, $HostArchitecture | Out-File -Encoding utf8 -FilePath $distributionXmlPath -Force
# Build final package path
$finalPackagePath = Join-Path $OutputDirectory "$PackageName-$Version-osx-$HostArchitecture.pkg"
# Rename x86_64 to x64 for compatibility
$packageArchName = if ($HostArchitecture -eq "x86_64") { "x64" } else { $HostArchitecture }
$finalPackagePath = Join-Path $OutputDirectory "$PackageName-$Version-osx-$packageArchName.pkg"
# Remove existing package if it exists
if (Test-Path $finalPackagePath) {
Write-Warning "Removing existing package: $finalPackagePath"
@@ -1542,7 +1544,7 @@ function New-MacOsDistributionPackage
--resources $resourcesDir `
$finalPackagePath
}
if (Test-Path $finalPackagePath) {
Write-Log "Successfully created macOS package: $finalPackagePath"
}
@@ -1612,7 +1614,7 @@ function New-RpmSpec
# RPM doesn't allow hyphens in version, so convert them to underscores
# 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")
# Don't use RPM macros - build the full release string in PowerShell
$rpmRelease = "$Iteration.$Distribution"
@@ -1638,7 +1640,7 @@ AutoReq: no
} else {
# For cross-architecture builds, don't specify BuildArch in spec
# The --target option will handle the architecture
# Disable automatic binary stripping for cross-arch builds
# The native /bin/strip on x86_64 cannot process ARM64 binaries and would fail with:
# "Unable to recognise the format of the input file"
@@ -1646,7 +1648,7 @@ AutoReq: no
# __strip: This macro controls the command used for stripping binaries during the build process.
# /bin/true: A command that does nothing and always exits successfully, effectively bypassing the stripping process.
$specContent += "%define __strip /bin/true`n"
# Disable debug package generation to prevent strip-related errors
# Debug packages require binary stripping which fails for cross-arch builds
# See: https://rpm-packaging-guide.github.io/#debugging
@@ -1903,11 +1905,11 @@ $(if ($extendedDescription) { $extendedDescription + "`n" })
# Copy DEBIAN directory and data files to build root
$buildDir = Join-Path $debBuildRoot "build"
New-Item -ItemType Directory -Path $buildDir -Force | Out-Null
Write-Verbose "debianDir: $debianDir" -Verbose
Write-Verbose "dataDir: $dataDir" -Verbose
Write-Verbose "buildDir: $buildDir" -Verbose
# Use cp to preserve symlinks
Start-NativeExecution { cp -a $debianDir "$buildDir/DEBIAN" }
Start-NativeExecution { cp -a $dataDir/* $buildDir }
@@ -2017,14 +2019,14 @@ function New-MacOSPackage
$linkDestDir = Join-Path $pkgRoot (Split-Path $link.Destination -Parent)
New-Item -ItemType Directory -Path $linkDestDir -Force | Out-Null
$finalLinkPath = Join-Path $pkgRoot $link.Destination
Write-Verbose "Creating symlink at $finalLinkPath" -Verbose
# Remove if exists
if (Test-Path $finalLinkPath) {
Remove-Item $finalLinkPath -Force
}
# Get the target of the original symlink and recreate it in the package root
if (Test-Path $link.Source) {
$linkTarget = (Get-Item $link.Source).Target
@@ -2050,10 +2052,10 @@ function New-MacOSPackage
# Build the component package using pkgbuild
$pkgIdentifier = Get-MacOSPackageId -IsPreview:($Name -like '*-preview')
if ($PSCmdlet.ShouldProcess("Build component package with pkgbuild")) {
Write-Log "Running pkgbuild to create component package..."
Start-NativeExecution -VerboseOutputOnError {
pkgbuild --root $pkgRoot `
--identifier $pkgIdentifier `
@@ -2062,7 +2064,7 @@ function New-MacOSPackage
--install-location "/" `
$componentPkgPath
}
Write-Verbose "Component package created: $componentPkgPath" -Verbose
}
@@ -2075,7 +2077,7 @@ function New-MacOSPackage
-HostArchitecture $HostArchitecture `
-PackageIdentifier $pkgIdentifier `
-IsPreview:($Name -like '*-preview')
return $distributionPackage
}
finally {