mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
[release/v7.4] Improve ADO package build and validation across platforms (#26405)
This commit is contained in:
@@ -1237,15 +1237,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 `
|
||||
@@ -1262,11 +1262,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"
|
||||
@@ -1275,7 +1275,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
|
||||
@@ -1288,16 +1288,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
|
||||
@@ -1335,7 +1335,7 @@ function New-UnixPackage {
|
||||
-AfterRemoveScript $AfterScriptInfo.AfterRemoveScript `
|
||||
-HostArchitecture $HostArchitecture `
|
||||
-CurrentLocation $CurrentLocation
|
||||
|
||||
|
||||
$Output = @("Created package {:path=>""$($result.PackageName)""}")
|
||||
}
|
||||
catch {
|
||||
@@ -1346,7 +1346,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
|
||||
@@ -1361,7 +1361,7 @@ function New-UnixPackage {
|
||||
HostArchitecture = $HostArchitecture
|
||||
CurrentLocation = $CurrentLocation
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$packageFile = New-MacOSPackage @macPkgArgs
|
||||
$Output = @("Created package {:path=>""$($packageFile.Name)""}")
|
||||
@@ -1384,7 +1384,7 @@ function New-UnixPackage {
|
||||
Clear-MacOSLauncher
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Clean up rpmbuild directory if it was created
|
||||
if ($Type -eq 'rpm') {
|
||||
$rpmBuildRoot = Join-Path $env:HOME "rpmbuild"
|
||||
@@ -1393,7 +1393,7 @@ function New-UnixPackage {
|
||||
Remove-Item -Path $rpmBuildRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($AfterScriptInfo.AfterInstallScript) {
|
||||
Remove-Item -ErrorAction 'silentlycontinue' $AfterScriptInfo.AfterInstallScript -Force
|
||||
}
|
||||
@@ -1487,13 +1487,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
|
||||
@@ -1509,7 +1509,7 @@ function New-MacOsDistributionPackage
|
||||
|
||||
# Minimum OS version
|
||||
$minOSVersion = "11.0" # macOS Big Sur minimum
|
||||
|
||||
|
||||
# format distribution template with:
|
||||
# 0 - title
|
||||
# 1 - version
|
||||
@@ -1520,8 +1520,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"
|
||||
@@ -1540,7 +1542,7 @@ function New-MacOsDistributionPackage
|
||||
--resources $resourcesDir `
|
||||
$finalPackagePath
|
||||
}
|
||||
|
||||
|
||||
if (Test-Path $finalPackagePath) {
|
||||
Write-Log "Successfully created macOS package: $finalPackagePath"
|
||||
}
|
||||
@@ -1610,7 +1612,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"
|
||||
@@ -1636,7 +1638,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"
|
||||
@@ -1644,7 +1646,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
|
||||
@@ -1901,11 +1903,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 }
|
||||
@@ -2015,14 +2017,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
|
||||
@@ -2048,10 +2050,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 `
|
||||
@@ -2060,7 +2062,7 @@ function New-MacOSPackage
|
||||
--install-location "/" `
|
||||
$componentPkgPath
|
||||
}
|
||||
|
||||
|
||||
Write-Verbose "Component package created: $componentPkgPath" -Verbose
|
||||
}
|
||||
|
||||
@@ -2073,7 +2075,7 @@ function New-MacOSPackage
|
||||
-HostArchitecture $HostArchitecture `
|
||||
-PackageIdentifier $pkgIdentifier `
|
||||
-IsPreview:($Name -like '*-preview')
|
||||
|
||||
|
||||
return $distributionPackage
|
||||
}
|
||||
finally {
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
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
|
||||
Push-Location $script:payloadDir
|
||||
try {
|
||||
$payloadFile = Join-Path $componentPkg.FullName "Payload"
|
||||
Get-Content -Path $payloadFile -Raw -AsByteStream | & cpio -i 2>&1 | Out-Null
|
||||
} finally {
|
||||
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 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 = @{
|
||||
"PowerShell executable" = "usr/local/microsoft/powershell/*/pwsh"
|
||||
"PowerShell symlink in /usr/local/bin" = "usr/local/bin/pwsh*"
|
||||
"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 += @{
|
||||
Description = $key
|
||||
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
|
||||
# Executable bit should be set
|
||||
$permissions.ToString() | Should -Match 'x' -Because "pwsh binary should have execute permissions"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 | 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user