From 03509788c8e89326879dbc7b6f4284138c1d014c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Oct 2025 19:21:07 +0000 Subject: [PATCH 01/26] Initial plan From 545c522b257ad80870107ab8ce14d6b2c6820a14 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Oct 2025 19:27:55 +0000 Subject: [PATCH 02/26] Replace fpm with native rpmbuild for RPM package generation Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- build.psm1 | 21 ++- tools/packaging/packaging.psm1 | 271 +++++++++++++++++++++++++++++---- 2 files changed, 256 insertions(+), 36 deletions(-) diff --git a/build.psm1 b/build.psm1 index 98d080ecc99..c72e11b1438 100644 --- a/build.psm1 +++ b/build.psm1 @@ -2401,11 +2401,24 @@ function Start-PSBootstrap { } # Install [fpm](https://github.com/jordansissel/fpm) + # Note: fpm is now only needed for DEB and macOS packages; RPM packages use rpmbuild directly if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') { - Install-GlobalGem -Sudo $sudo -GemName "dotenv" -GemVersion "2.8.1" - Install-GlobalGem -Sudo $sudo -GemName "ffi" -GemVersion "1.16.3" - Install-GlobalGem -Sudo $sudo -GemName "fpm" -GemVersion "1.15.1" - Install-GlobalGem -Sudo $sudo -GemName "rexml" -GemVersion "3.2.5" + # Only install fpm on Debian-based systems and macOS + if ($environment.IsDebianFamily -or $environment.IsMacOS) { + Install-GlobalGem -Sudo $sudo -GemName "dotenv" -GemVersion "2.8.1" + Install-GlobalGem -Sudo $sudo -GemName "ffi" -GemVersion "1.16.3" + Install-GlobalGem -Sudo $sudo -GemName "fpm" -GemVersion "1.15.1" + Install-GlobalGem -Sudo $sudo -GemName "rexml" -GemVersion "3.2.5" + } + + # For RPM-based systems, ensure rpmbuild is available + if ($environment.IsRedHatFamily -or $environment.IsSUSEFamily) { + Write-Verbose -Verbose "Checking for rpmbuild..." + if (!(Get-Command rpmbuild -ErrorAction SilentlyContinue)) { + Write-Warning "rpmbuild not found. Installing rpm-build package..." + Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo $PackageManager install -y rpm-build")) -IgnoreExitcode + } + } } } diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 8e7fb3c4e08..cc3168d7cdf 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1245,40 +1245,102 @@ function New-UnixPackage { # Setup package dependencies $Dependencies = @(Get-PackageDependencies @packageDependenciesParams) - $Arguments = @() - - - $Arguments += Get-FpmArguments ` - -Name $Name ` - -Version $packageVersion ` - -Iteration $Iteration ` - -Description $Description ` - -Type $Type ` - -Dependencies $Dependencies ` - -AfterInstallScript $AfterScriptInfo.AfterInstallScript ` - -AfterRemoveScript $AfterScriptInfo.AfterRemoveScript ` - -Staging $Staging ` - -Destination $Destination ` - -ManGzipFile $ManGzipInfo.GzipFile ` - -ManDestination $ManGzipInfo.ManFile ` - -LinkInfo $Links ` - -AppsFolder $AppsFolder ` - -Distribution $DebDistro ` - -HostArchitecture $HostArchitecture ` - -ErrorAction Stop - # Build package try { - if ($PSCmdlet.ShouldProcess("Create $type package")) { - Write-Log "Creating package with fpm $Arguments..." - try { - $Output = Start-NativeExecution { fpm $Arguments } + if ($Type -eq 'rpm') { + # 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 ` + -Version $packageVersion ` + -Iteration $Iteration ` + -Description $Description ` + -Dependencies $Dependencies ` + -AfterInstallScript $AfterScriptInfo.AfterInstallScript ` + -AfterRemoveScript $AfterScriptInfo.AfterRemoveScript ` + -Staging $Staging ` + -Destination $Destination ` + -ManGzipFile $ManGzipInfo.GzipFile ` + -ManDestination $ManGzipInfo.ManFile ` + -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 + + # Build RPM package + try { + $Output = Start-NativeExecution { + rpmbuild -bb --quiet --define "_topdir $rpmBuildRoot" --buildroot "$rpmBuildRoot/BUILDROOT" $specFile + } + + # Find the generated RPM + $rpmFile = Get-ChildItem -Path (Join-Path $rpmsDir $HostArchitecture) -Filter "*.rpm" | + Sort-Object -Property LastWriteTime -Descending | + Select-Object -First 1 + + if ($rpmFile) { + # Copy RPM to current location + Copy-Item -Path $rpmFile.FullName -Destination $CurrentLocation -Force + $Output = @("Created package {:path=>""$(Join-Path $CurrentLocation $rpmFile.Name)""}") + } else { + throw "RPM file not found after build" + } + } + catch { + Write-Verbose -Message "!!!Handling error in rpmbuild!!!" -Verbose -ErrorAction SilentlyContinue + Write-Verbose -Message "$Output" -Verbose -ErrorAction SilentlyContinue + Get-Error -InputObject $_ + throw + } } - catch { - Write-Verbose -Message "!!!Handling error in FPM!!!" -Verbose -ErrorAction SilentlyContinue - Write-Verbose -Message "$Output" -Verbose -ErrorAction SilentlyContinue - Get-Error -InputObject $_ - throw + } else { + # Use fpm for DEB and macOS packages + $Arguments = @() + + $Arguments += Get-FpmArguments ` + -Name $Name ` + -Version $packageVersion ` + -Iteration $Iteration ` + -Description $Description ` + -Type $Type ` + -Dependencies $Dependencies ` + -AfterInstallScript $AfterScriptInfo.AfterInstallScript ` + -AfterRemoveScript $AfterScriptInfo.AfterRemoveScript ` + -Staging $Staging ` + -Destination $Destination ` + -ManGzipFile $ManGzipInfo.GzipFile ` + -ManDestination $ManGzipInfo.ManFile ` + -LinkInfo $Links ` + -AppsFolder $AppsFolder ` + -Distribution $DebDistro ` + -HostArchitecture $HostArchitecture ` + -ErrorAction Stop + + if ($PSCmdlet.ShouldProcess("Create $type package")) { + Write-Log "Creating package with fpm $Arguments..." + try { + $Output = Start-NativeExecution { fpm $Arguments } + } + catch { + Write-Verbose -Message "!!!Handling error in FPM!!!" -Verbose -ErrorAction SilentlyContinue + Write-Verbose -Message "$Output" -Verbose -ErrorAction SilentlyContinue + Get-Error -InputObject $_ + throw + } } } } finally { @@ -1439,6 +1501,142 @@ Class LinkInfo [string] $Destination } +function New-RpmSpec +{ + param( + [Parameter(Mandatory,HelpMessage='Package Name')] + [String]$Name, + + [Parameter(Mandatory,HelpMessage='Package Version')] + [String]$Version, + + [Parameter(Mandatory)] + [String]$Iteration, + + [Parameter(Mandatory,HelpMessage='Package description')] + [String]$Description, + + [Parameter(Mandatory,HelpMessage='Staging folder for installation files')] + [String]$Staging, + + [Parameter(Mandatory,HelpMessage='Install path on target machine')] + [String]$Destination, + + [Parameter(Mandatory,HelpMessage='The built and gzipped man file.')] + [String]$ManGzipFile, + + [Parameter(Mandatory,HelpMessage='The destination of the man file')] + [String]$ManDestination, + + [Parameter(Mandatory,HelpMessage='Symlink to powershell executable')] + [LinkInfo[]]$LinkInfo, + + [Parameter(Mandatory,HelpMessage='Packages required to install this package')] + [String[]]$Dependencies, + + [Parameter(Mandatory,HelpMessage='Script to run after the package installation.')] + [String]$AfterInstallScript, + + [Parameter(Mandatory,HelpMessage='Script to run after the package removal.')] + [String]$AfterRemoveScript, + + [String]$Distribution = 'rhel.7', + [string]$HostArchitecture + ) + + $specContent = @" +# RPM spec file for PowerShell +# Generated by PowerShell build system + +Name: $Name +Version: $Version +Release: $Iteration%{?dist} +Summary: PowerShell - Cross-platform automation and configuration tool/framework +License: MIT +URL: https://microsoft.com/powershell +BuildArch: $HostArchitecture +AutoReq: no + +"@ + + # Add dependencies + foreach ($dep in $Dependencies) { + $specContent += "Requires: $dep`n" + } + + $specContent += @" + +%description +$Description + +%prep +# No prep needed - files are already staged + +%build +# No build needed - binaries are pre-built + +%install +rm -rf `$RPM_BUILD_ROOT +mkdir -p `$RPM_BUILD_ROOT$Destination +mkdir -p `$RPM_BUILD_ROOT$(Split-Path -Parent $ManDestination) + +# Copy all files from staging to destination +cp -r $Staging/* `$RPM_BUILD_ROOT$Destination/ + +# Copy man page +cp $ManGzipFile `$RPM_BUILD_ROOT$ManDestination + +"@ + + # Add symlinks + foreach ($link in $LinkInfo) { + $linkDir = Split-Path -Parent $link.Destination + $specContent += "mkdir -p `$RPM_BUILD_ROOT$linkDir`n" + $specContent += "ln -sf $($link.Source) `$RPM_BUILD_ROOT$($link.Destination)`n" + } + + # Post-install script + $postInstallContent = Get-Content -Path $AfterInstallScript -Raw + $specContent += @" + +%post +$postInstallContent + +"@ + + # Post-uninstall script + $postUninstallContent = Get-Content -Path $AfterRemoveScript -Raw + $specContent += @" +%postun +$postUninstallContent + +"@ + + # Files section + $specContent += @" +%files +%defattr(-,root,root,-) +$Destination/* +$ManDestination + +"@ + + # Add symlinks to files + foreach ($link in $LinkInfo) { + $specContent += "$($link.Destination)`n" + } + + $specContent += @" + +%changelog +* $(Get-Date -Format "ddd MMM dd yyyy") PowerShell Team - $Version-$Iteration +- Automated build + +"@ + + return $specContent +} + function Get-FpmArguments { param( @@ -1651,7 +1849,16 @@ function Get-PackageDependencies function Test-Dependencies { - foreach ($Dependency in "fpm") { + # Note: RPM packages no longer require fpm; they use rpmbuild directly + # DEB packages still use fpm + $Dependencies = @() + + # Only check for fpm on Debian-based systems + if ($Environment.IsDebianFamily) { + $Dependencies += "fpm" + } + + foreach ($Dependency in $Dependencies) { if (!(precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Scenario Package")) { # These tools are not added to the path automatically on OpenSUSE 13.2 # try adding them to the path and re-tesing first From a9b663233a5c7bf9f4e224a30be875871961e305 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Oct 2025 19:31:16 +0000 Subject: [PATCH 03/26] Fix RPM spec file generation and cleanup Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 56 ++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index cc3168d7cdf..615e7c5d96d 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1357,6 +1357,16 @@ function New-UnixPackage { Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo mv $hack_dest $symlink_dest")) -VerboseOutputOnError } } + + # Clean up rpmbuild directory if it was created + if ($Type -eq 'rpm') { + $rpmBuildRoot = Join-Path $env:HOME "rpmbuild" + if (Test-Path $rpmBuildRoot) { + Write-Verbose "Cleaning up rpmbuild directory: $rpmBuildRoot" -Verbose + Remove-Item -Path $rpmBuildRoot -Recurse -Force -ErrorAction SilentlyContinue + } + } + if ($AfterScriptInfo.AfterInstallScript) { Remove-Item -ErrorAction 'silentlycontinue' $AfterScriptInfo.AfterInstallScript -Force } @@ -1588,51 +1598,43 @@ cp $ManGzipFile `$RPM_BUILD_ROOT$ManDestination "@ - # Add symlinks + # Add symlinks - we need to get the target of the temp symlink foreach ($link in $LinkInfo) { $linkDir = Split-Path -Parent $link.Destination $specContent += "mkdir -p `$RPM_BUILD_ROOT$linkDir`n" - $specContent += "ln -sf $($link.Source) `$RPM_BUILD_ROOT$($link.Destination)`n" + # For RPM, we copy the symlink itself (which fpm does by including it in the source) + # The symlink at $link.Source points to the actual target, so we'll copy it + $specContent += "cp -P $($link.Source) `$RPM_BUILD_ROOT$($link.Destination)`n" } # Post-install script $postInstallContent = Get-Content -Path $AfterInstallScript -Raw - $specContent += @" - -%post -$postInstallContent - -"@ + $specContent += "`n%post`n" + $specContent += $postInstallContent + $specContent += "`n" # Post-uninstall script $postUninstallContent = Get-Content -Path $AfterRemoveScript -Raw - $specContent += @" -%postun -$postUninstallContent - -"@ + $specContent += "%postun`n" + $specContent += $postUninstallContent + $specContent += "`n" # Files section - $specContent += @" -%files -%defattr(-,root,root,-) -$Destination/* -$ManDestination - -"@ + $specContent += "%files`n" + $specContent += "%defattr(-,root,root,-)`n" + $specContent += "$Destination/*`n" + $specContent += "$ManDestination`n" # Add symlinks to files foreach ($link in $LinkInfo) { $specContent += "$($link.Destination)`n" } - $specContent += @" - -%changelog -* $(Get-Date -Format "ddd MMM dd yyyy") PowerShell Team - $Version-$Iteration -- Automated build - -"@ + # Changelog with correct date format for RPM + $changelogDate = Get-Date -Format "ddd MMM dd yyyy" + $specContent += "`n%changelog`n" + $specContent += "* $changelogDate PowerShell Team - $Version-$Iteration`n" + $specContent += "- Automated build`n" return $specContent } From e8a1be042d1c37d5ba4fa7ab9257c8b183034cea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Oct 2025 19:40:45 +0000 Subject: [PATCH 04/26] Fix rpmbuild command execution and verify with comprehensive test Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 615e7c5d96d..1395d48505a 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1283,12 +1283,18 @@ function New-UnixPackage { # Build RPM package try { - $Output = Start-NativeExecution { - rpmbuild -bb --quiet --define "_topdir $rpmBuildRoot" --buildroot "$rpmBuildRoot/BUILDROOT" $specFile + # Use bash to properly handle rpmbuild arguments + $buildCmd = "rpmbuild -bb --quiet --define '_topdir $rpmBuildRoot' --buildroot '$rpmBuildRoot/BUILDROOT' '$specFile'" + 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" | + $rpmFile = Get-ChildItem -Path (Join-Path $rpmsDir $HostArchitecture) -Filter "*.rpm" -ErrorAction Stop | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 @@ -1302,7 +1308,9 @@ function New-UnixPackage { } catch { Write-Verbose -Message "!!!Handling error in rpmbuild!!!" -Verbose -ErrorAction SilentlyContinue - Write-Verbose -Message "$Output" -Verbose -ErrorAction SilentlyContinue + if ($Output) { + Write-Verbose -Message "$Output" -Verbose -ErrorAction SilentlyContinue + } Get-Error -InputObject $_ throw } From 21511f9e41c3293b4cf5e86b6ebd6f23fcdecdc3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 17:57:15 +0000 Subject: [PATCH 05/26] Update linux packaging workflow to explicitly import packaging.psm1 This ensures the RPM packaging changes (rpmbuild instead of fpm) are properly loaded in the Linux packaging GitHub Actions workflow, similar to how the Windows packaging workflow imports modules. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- .github/actions/test/linux-packaging/action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/actions/test/linux-packaging/action.yml b/.github/actions/test/linux-packaging/action.yml index 736bddfa7a7..cb0c0aac844 100644 --- a/.github/actions/test/linux-packaging/action.yml +++ b/.github/actions/test/linux-packaging/action.yml @@ -62,6 +62,9 @@ runs: # Create the artifacts staging directory New-Item -ItemType Directory -Path "$env:BUILD_ARTIFACTSTAGINGDIRECTORY" -Force | Out-Null + # Import packaging module to ensure RPM packaging changes are loaded + Import-Module ./build.psm1 -Force + Import-Module ./tools/packaging/packaging.psm1 -Force Import-Module ./tools/ci.psm1 Restore-PSOptions -PSOptionsPath '${{ runner.workspace }}/build/psoptions.json' $options = (Get-PSOptions) From 3dbd3569396dc99855b91a95d965151bb45d7e0d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:08:41 +0000 Subject: [PATCH 06/26] Update Linux CI workflow to trigger packaging job for packaging changes - Add packagingChanged output to changes job (matching Windows CI pattern) - Update linux_packaging job to only run when packagingChanged == 'true' - Remove ci_build dependency from linux_packaging job (matching Windows CI pattern) This ensures the Linux packaging job runs when packaging-related files change (tools/packaging/, build.psm1, etc.) similar to how the Windows packaging job is triggered. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- .github/workflows/linux-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 2e4c54d4068..c7016f0c123 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -53,6 +53,7 @@ jobs: # Set job outputs to values from filter step outputs: source: ${{ steps.filter.outputs.source }} + packagingChanged: ${{ steps.filter.outputs.packagingChanged }} steps: - name: checkout uses: actions/checkout@v5 @@ -237,9 +238,8 @@ jobs: linux_packaging: name: Linux Packaging needs: - - ci_build - changes - if: ${{ needs.changes.outputs.source == 'true' }} + if: ${{ needs.changes.outputs.packagingChanged == 'true' }} runs-on: ubuntu-latest steps: - name: checkout From 42ace2345eafa0d243dedce3ebe66bc3860cc712 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:12:02 +0000 Subject: [PATCH 07/26] Add back ci_build dependency to linux_packaging job The Linux packaging job requires ci_build as a dependency to ensure the build artifacts are available before packaging. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- .github/workflows/linux-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index c7016f0c123..b6df3119418 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -238,6 +238,7 @@ jobs: linux_packaging: name: Linux Packaging needs: + - ci_build - changes if: ${{ needs.changes.outputs.packagingChanged == 'true' }} runs-on: ubuntu-latest From 08cd012c4128d24b7142ee87f093c1aa8f48e325 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:28:39 +0000 Subject: [PATCH 08/26] Fix macOS and Linux packaging issues 1. macOS issue: Fixed IsDebianFamily check by adding IsLinux condition - IsDebianFamily property only exists on Linux systems - Now checks ($environment.IsLinux -and $environment.IsDebianFamily) 2. Linux RPM issue: Fixed version formatting for RPM spec files - RPM spec files don't allow hyphens in Version field - Split version like "7.6.0-preview.6" into Version: 7.6.0 and Release: 1.preview.6 - Pre-release identifier moved from Version to Release field per RPM conventions Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- build.psm1 | 4 ++-- tools/packaging/packaging.psm1 | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/build.psm1 b/build.psm1 index c72e11b1438..89c1506b8fa 100644 --- a/build.psm1 +++ b/build.psm1 @@ -2404,7 +2404,7 @@ function Start-PSBootstrap { # Note: fpm is now only needed for DEB and macOS packages; RPM packages use rpmbuild directly if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') { # Only install fpm on Debian-based systems and macOS - if ($environment.IsDebianFamily -or $environment.IsMacOS) { + if (($environment.IsLinux -and $environment.IsDebianFamily) -or $environment.IsMacOS) { Install-GlobalGem -Sudo $sudo -GemName "dotenv" -GemVersion "2.8.1" Install-GlobalGem -Sudo $sudo -GemName "ffi" -GemVersion "1.16.3" Install-GlobalGem -Sudo $sudo -GemName "fpm" -GemVersion "1.15.1" @@ -2412,7 +2412,7 @@ function Start-PSBootstrap { } # For RPM-based systems, ensure rpmbuild is available - if ($environment.IsRedHatFamily -or $environment.IsSUSEFamily) { + if ($environment.IsLinux -and ($environment.IsRedHatFamily -or $environment.IsSUSEFamily)) { Write-Verbose -Verbose "Checking for rpmbuild..." if (!(Get-Command rpmbuild -ErrorAction SilentlyContinue)) { Write-Warning "rpmbuild not found. Installing rpm-build package..." diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 1395d48505a..9683ee94749 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1562,13 +1562,25 @@ function New-RpmSpec [string]$HostArchitecture ) + # RPM doesn't allow hyphens in version, so split version and pre-release + # e.g., "7.6.0-preview.6" becomes Version: 7.6.0 and Release: 1.preview.6 + $rpmVersion = $Version + $rpmRelease = $Iteration + + if ($Version -match '^(.+?)-(.+)$') { + $rpmVersion = $Matches[1] + $preRelease = $Matches[2] + # Prepend iteration to pre-release part + $rpmRelease = "$Iteration.$preRelease" + } + $specContent = @" # RPM spec file for PowerShell # Generated by PowerShell build system Name: $Name -Version: $Version -Release: $Iteration%{?dist} +Version: $rpmVersion +Release: $rpmRelease%{?dist} Summary: PowerShell - Cross-platform automation and configuration tool/framework License: MIT URL: https://microsoft.com/powershell @@ -1641,7 +1653,7 @@ cp $ManGzipFile `$RPM_BUILD_ROOT$ManDestination # Changelog with correct date format for RPM $changelogDate = Get-Date -Format "ddd MMM dd yyyy" $specContent += "`n%changelog`n" - $specContent += "* $changelogDate PowerShell Team - $Version-$Iteration`n" + $specContent += "* $changelogDate PowerShell Team - $rpmVersion-$rpmRelease`n" $specContent += "- Automated build`n" return $specContent From 0415c7f8d8991e68389c6b56e6f8d688f15b8dad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 18:57:16 +0000 Subject: [PATCH 09/26] Fix RPM package path duplication issue The output path was being created with Join-Path twice: 1. Once when creating the output message (line 1304) 2. Again when parsing the output (line 1388) This caused the path to be duplicated like: /home/runner/work/PowerShell/PowerShell/home/runner/work/PowerShell/PowerShell/file.rpm Fixed by using just the file name in the output message, allowing the existing Join-Path in line 1388 to create the correct full path. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 9683ee94749..12f3016d1d0 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1301,7 +1301,7 @@ function New-UnixPackage { if ($rpmFile) { # Copy RPM to current location Copy-Item -Path $rpmFile.FullName -Destination $CurrentLocation -Force - $Output = @("Created package {:path=>""$(Join-Path $CurrentLocation $rpmFile.Name)""}") + $Output = @("Created package {:path=>""$($rpmFile.Name)""}") } else { throw "RPM file not found after build" } From fad01c25b2a8f297d78d5c4f38766ed338f9594e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 19:12:14 +0000 Subject: [PATCH 10/26] Fix cross-architecture RPM builds with --target option When building RPMs for non-native architectures (e.g., aarch64 on x86_64), rpmbuild needs the --target option to specify the target architecture. Without this, rpmbuild fails with "No compatible architectures found for build". Added --target parameter for non-x86_64 and non-noarch builds to support cross-architecture RPM package generation (e.g., rpm-fxdependent-arm64). Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 12f3016d1d0..07eb3a3a34e 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1284,7 +1284,12 @@ function New-UnixPackage { # Build RPM package try { # Use bash to properly handle rpmbuild arguments - $buildCmd = "rpmbuild -bb --quiet --define '_topdir $rpmBuildRoot' --buildroot '$rpmBuildRoot/BUILDROOT' '$specFile'" + # Add --target for cross-architecture builds + $targetArch = "" + if ($HostArchitecture -ne "x86_64" -and $HostArchitecture -ne "noarch") { + $targetArch = "--target $HostArchitecture" + } + $buildCmd = "rpmbuild -bb --quiet $targetArch --define '_topdir $rpmBuildRoot' --buildroot '$rpmBuildRoot/BUILDROOT' '$specFile'" Write-Verbose "Running: $buildCmd" -Verbose $Output = bash -c $buildCmd 2>&1 $exitCode = $LASTEXITCODE From a1803b8362cd68458a7e46cc04e7b6dc7d2b825b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 19:26:15 +0000 Subject: [PATCH 11/26] Fix cross-architecture RPM builds by omitting BuildArch for non-native architectures When building RPMs for non-native architectures (e.g., aarch64 on x86_64), the BuildArch directive in the spec file conflicts with the --target option, causing "No compatible architectures found for build" errors. Solution: Only include BuildArch directive for x86_64 and noarch packages. For cross-architecture builds (like aarch64), omit BuildArch from the spec file and let the --target option control the architecture. This allows rpmbuild to package pre-built binaries for different architectures without requiring cross-compilation toolchains. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 07eb3a3a34e..968be1eefc5 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1589,11 +1589,20 @@ Release: $rpmRelease%{?dist} Summary: PowerShell - Cross-platform automation and configuration tool/framework License: MIT URL: https://microsoft.com/powershell -BuildArch: $HostArchitecture AutoReq: no "@ + # Only add BuildArch if not doing cross-architecture build + # For cross-arch builds, we'll rely on --target option + if ($HostArchitecture -eq "x86_64" -or $HostArchitecture -eq "noarch") { + $specContent += "BuildArch: $HostArchitecture`n`n" + } else { + # For cross-architecture builds, don't specify BuildArch in spec + # The --target option will handle the architecture + $specContent += "`n" + } + # Add dependencies foreach ($dep in $Dependencies) { $specContent += "Requires: $dep`n" From 9473c913641c8835cec812c2df9d93f56df4fa0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 19:49:06 +0000 Subject: [PATCH 12/26] Fix package artifacts handling for GitHub Actions - Detect GitHub Actions environment and use correct artifacts directory path - Use GITHUB_WORKSPACE/../packages for GitHub Actions instead of BUILD_ARTIFACTSTAGINGDIRECTORY - Ensure artifacts directory exists before copying packages - Preserve packages in artifacts directory even when git clean runs for Raspbian build - Maintain backward compatibility with Azure DevOps workflows Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/ci.psm1 | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tools/ci.psm1 b/tools/ci.psm1 index 7b13ded1811..9e95e68c843 100644 --- a/tools/ci.psm1 +++ b/tools/ci.psm1 @@ -874,16 +874,36 @@ function New-LinuxPackage $packageObj = $package } - Write-Log -message "Artifacts directory: ${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" - Copy-Item $packageObj.FullName -Destination "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" -Force + # Determine artifacts directory (GitHub Actions or Azure DevOps) + $artifactsDir = if ($env:GITHUB_ACTIONS -eq 'true') { + "${env:GITHUB_WORKSPACE}/../packages" + } else { + "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" + } + + # Ensure artifacts directory exists + if (-not (Test-Path $artifactsDir)) { + New-Item -ItemType Directory -Path $artifactsDir -Force | Out-Null + } + + Write-Log -message "Artifacts directory: $artifactsDir" + Copy-Item $packageObj.FullName -Destination $artifactsDir -Force } if ($IsLinux) { + # Determine artifacts directory (GitHub Actions or Azure DevOps) + $artifactsDir = if ($env:GITHUB_ACTIONS -eq 'true') { + "${env:GITHUB_WORKSPACE}/../packages" + } else { + "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" + } + # Create and package Raspbian .tgz + # Build must be clean for Raspbian Start-PSBuild -PSModuleRestore -Clean -Runtime linux-arm -Configuration 'Release' $armPackage = Start-PSPackage @packageParams -Type tar-arm -SkipReleaseChecks - Copy-Item $armPackage -Destination "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" -Force + Copy-Item $armPackage -Destination $artifactsDir -Force } } From 6a78c107b94bbaa1d4dba3837e7ec41601e42fe0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 20:19:23 +0000 Subject: [PATCH 13/26] Add Pester tests for Linux package name validation - Created test/packaging/linux/package-validation.tests.ps1 - Validates RPM package names match expected format (powershell-X.Y.Z-1.preview.N.dist.arch.rpm) - Validates tar.gz package names match expected format (powershell-X.Y.Z-platform-runtime.tar.gz) - Detects GitHub Actions vs Azure DevOps artifact directories - Added test step to Linux packaging workflow to run validation after package creation - Tests run before artifact upload to catch issues early Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- .../actions/test/linux-packaging/action.yml | 10 +++ .../linux/package-validation.tests.ps1 | 81 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 test/packaging/linux/package-validation.tests.ps1 diff --git a/.github/actions/test/linux-packaging/action.yml b/.github/actions/test/linux-packaging/action.yml index cb0c0aac844..b7bbdf37185 100644 --- a/.github/actions/test/linux-packaging/action.yml +++ b/.github/actions/test/linux-packaging/action.yml @@ -78,6 +78,16 @@ runs: Invoke-CIFinish shell: pwsh + - name: Validate Package Names + run: |- + # Run Pester tests to validate package names + Import-Module Pester -Force + $testResults = Invoke-Pester -Path ./test/packaging/linux/package-validation.tests.ps1 -PassThru + if ($testResults.FailedCount -gt 0) { + throw "Package validation tests failed" + } + shell: pwsh + - name: Upload deb packages uses: actions/upload-artifact@v4 with: diff --git a/test/packaging/linux/package-validation.tests.ps1 b/test/packaging/linux/package-validation.tests.ps1 new file mode 100644 index 00000000000..0c535447592 --- /dev/null +++ b/test/packaging/linux/package-validation.tests.ps1 @@ -0,0 +1,81 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Describe "Linux Package Name Validation" { + BeforeAll { + # Determine artifacts directory (GitHub Actions or Azure DevOps) + $artifactsDir = if ($env:GITHUB_ACTIONS -eq 'true') { + "$env:GITHUB_WORKSPACE/../packages" + } else { + $env:SYSTEM_ARTIFACTSDIRECTORY + } + + if (-not $artifactsDir) { + throw "Artifacts directory not found. GITHUB_WORKSPACE or SYSTEM_ARTIFACTSDIRECTORY must be set." + } + + Write-Verbose "Artifacts directory: $artifactsDir" -Verbose + } + + Context "RPM Package Names" { + It "Should have valid RPM package names" { + $rpmPackages = Get-ChildItem -Path $artifactsDir -Recurse -Filter *.rpm -ErrorAction SilentlyContinue + + if ($rpmPackages.Count -eq 0) { + Set-ItResult -Skipped -Because "No RPM packages found in artifacts directory" + return + } + + $invalidPackages = @() + foreach ($package in $rpmPackages) { + # Pattern matches: powershell-preview-7.6.0-1.preview.6.x86_64.rpm or powershell-7.6.0-1.x86_64.rpm + # Also matches: powershell-lts-7.6.0-1.rh.x86_64.rpm + if ($package.Name -notmatch 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(preview\.\d+\.)?(rh|cm)\.(x86_64|aarch64)\.rpm') { + $invalidPackages += "$($package.Name) is not a valid RPM package name" + Write-Warning "$($package.Name) is not a valid RPM package name" + } + } + + if ($invalidPackages.Count -gt 0) { + throw ($invalidPackages | Out-String) + } + + $rpmPackages.Count | Should -BeGreaterThan 0 + } + } + + Context "Tar.Gz Package Names" { + It "Should have valid tar.gz package names" { + $tarPackages = Get-ChildItem -Path $artifactsDir -Recurse -Filter *.tar.gz -ErrorAction SilentlyContinue + + if ($tarPackages.Count -eq 0) { + Set-ItResult -Skipped -Because "No tar.gz packages found in artifacts directory" + return + } + + $invalidPackages = @() + foreach ($package in $tarPackages) { + # Pattern matches: powershell-7.6.0-preview.6-linux-x64.tar.gz or powershell-7.6.0-linux-x64.tar.gz + # Also matches various runtime configurations + if ($package.Name -notmatch 'powershell-(lts-)?\d+\.\d+\.\d+\-([a-z]*.\d+\-)?(linux|osx|linux-musl)+\-(x64\-fxdependent|x64|arm32|arm64|x64\-musl-noopt\-fxdependent)\.(tar\.gz)') { + $invalidPackages += "$($package.Name) is not a valid tar.gz package name" + Write-Warning "$($package.Name) is not a valid tar.gz package name" + } + } + + if ($invalidPackages.Count -gt 0) { + throw ($invalidPackages | Out-String) + } + + $tarPackages.Count | Should -BeGreaterThan 0 + } + } + + Context "Package Existence" { + It "Should find at least one package in artifacts directory" { + $allPackages = Get-ChildItem -Path $artifactsDir -Recurse -Include *.rpm, *.tar.gz, *.deb -ErrorAction SilentlyContinue + + $allPackages.Count | Should -BeGreaterThan 0 -Because "At least one package should exist in the artifacts directory" + } + } +} From 16fb48bda6553c3567afbc802772b1d43adf77b2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 21:11:53 +0000 Subject: [PATCH 14/26] Fix RPM package name validation regex Removed incorrect (preview\.\d+\.)? pattern that was expecting preview.N. in the release field. Actual RPM packages have the preview identifier in the version field (e.g., 7.6.0_preview.5) not in the release field. The correct pattern now matches: - powershell-preview-7.6.0_preview.5-1.cm.x86_64.rpm - powershell-7.6.0-1.rh.x86_64.rpm - powershell-lts-7.6.0-1.cm.aarch64.rpm Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- test/packaging/linux/package-validation.tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/packaging/linux/package-validation.tests.ps1 b/test/packaging/linux/package-validation.tests.ps1 index 0c535447592..e0c9b41296c 100644 --- a/test/packaging/linux/package-validation.tests.ps1 +++ b/test/packaging/linux/package-validation.tests.ps1 @@ -28,9 +28,9 @@ Describe "Linux Package Name Validation" { $invalidPackages = @() foreach ($package in $rpmPackages) { - # Pattern matches: powershell-preview-7.6.0-1.preview.6.x86_64.rpm or powershell-7.6.0-1.x86_64.rpm - # Also matches: powershell-lts-7.6.0-1.rh.x86_64.rpm - if ($package.Name -notmatch 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(preview\.\d+\.)?(rh|cm)\.(x86_64|aarch64)\.rpm') { + # Pattern matches: powershell-preview-7.6.0_preview.5-1.cm.x86_64.rpm + # Also matches: powershell-7.6.0-1.rh.x86_64.rpm, powershell-lts-7.6.0-1.rh.x86_64.rpm + if ($package.Name -notmatch 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(rh|cm)\.(x86_64|aarch64)\.rpm') { $invalidPackages += "$($package.Name) is not a valid RPM package name" Write-Warning "$($package.Name) is not a valid RPM package name" } From 3c6d2e62c5f75f6cd6791820d5e21090a89cd6ed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 21:24:03 +0000 Subject: [PATCH 15/26] Simplify RPM version handling - just convert hyphens to underscores Instead of splitting version and moving pre-release to iteration field, simply convert hyphens to underscores in the version string. This produces: - Version: 7.6.0_preview.5 - Release: 1.cm Instead of the previous: - Version: 7.6.0 - Release: 1.cm.preview.5 Matches actual RPM package naming convention. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 968be1eefc5..0090d5a66f4 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1567,17 +1567,10 @@ function New-RpmSpec [string]$HostArchitecture ) - # RPM doesn't allow hyphens in version, so split version and pre-release - # e.g., "7.6.0-preview.6" becomes Version: 7.6.0 and Release: 1.preview.6 - $rpmVersion = $Version + # 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 '-', '_' $rpmRelease = $Iteration - - if ($Version -match '^(.+?)-(.+)$') { - $rpmVersion = $Matches[1] - $preRelease = $Matches[2] - # Prepend iteration to pre-release part - $rpmRelease = "$Iteration.$preRelease" - } $specContent = @" # RPM spec file for PowerShell From 563431c580ded57d4b239ef14d52c32f997583ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 21:29:48 +0000 Subject: [PATCH 16/26] Revert "Fix RPM package name validation regex" This reverts commit 16fb48bda6553c3567afbc802772b1d43adf77b2. The new naming scheme (7.6.0_preview.5) now matches the original validation pattern, so the regex change is no longer needed. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- test/packaging/linux/package-validation.tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/packaging/linux/package-validation.tests.ps1 b/test/packaging/linux/package-validation.tests.ps1 index e0c9b41296c..0c535447592 100644 --- a/test/packaging/linux/package-validation.tests.ps1 +++ b/test/packaging/linux/package-validation.tests.ps1 @@ -28,9 +28,9 @@ Describe "Linux Package Name Validation" { $invalidPackages = @() foreach ($package in $rpmPackages) { - # Pattern matches: powershell-preview-7.6.0_preview.5-1.cm.x86_64.rpm - # Also matches: powershell-7.6.0-1.rh.x86_64.rpm, powershell-lts-7.6.0-1.rh.x86_64.rpm - if ($package.Name -notmatch 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(rh|cm)\.(x86_64|aarch64)\.rpm') { + # Pattern matches: powershell-preview-7.6.0-1.preview.6.x86_64.rpm or powershell-7.6.0-1.x86_64.rpm + # Also matches: powershell-lts-7.6.0-1.rh.x86_64.rpm + if ($package.Name -notmatch 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(preview\.\d+\.)?(rh|cm)\.(x86_64|aarch64)\.rpm') { $invalidPackages += "$($package.Name) is not a valid RPM package name" Write-Warning "$($package.Name) is not a valid RPM package name" } From 4ef96b97e3375111d43df7288e3fda3f20bbc459 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 21:55:10 +0000 Subject: [PATCH 17/26] Add RPM spec file logging with GitHub Actions group support - Log spec file content after generation - Use GitHub Actions workflow commands (::group::) for collapsible output - Falls back to verbose logging in non-GitHub environments - Helps with debugging RPM package builds Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 0090d5a66f4..cc6148d4162 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1281,6 +1281,15 @@ function New-UnixPackage { $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" + Write-Host $specContent + Write-Host "::endgroup::" + } else { + Write-Verbose "RPM Spec File Content:`n$specContent" -Verbose + } + # Build RPM package try { # Use bash to properly handle rpmbuild arguments From fa902b53d5b1403d30acb0690f7b2e910ed53ec8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 18:19:01 +0000 Subject: [PATCH 18/26] Remove RPM macro and build release field with distribution suffix Instead of using %{?dist} RPM macro, build the complete release string in PowerShell by concatenating iteration and distribution. This ensures the distribution (e.g., "cm" or "rh") appears in the release field and matches the package filename. For example: Release: 1.cm produces powershell-preview-7.6.0_preview.5-1.cm.x86_64.rpm Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index cc6148d4162..51161c3947c 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1579,7 +1579,10 @@ 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 '-', '_' - $rpmRelease = $Iteration + + # 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" $specContent = @" # RPM spec file for PowerShell @@ -1587,7 +1590,7 @@ function New-RpmSpec Name: $Name Version: $rpmVersion -Release: $rpmRelease%{?dist} +Release: $rpmRelease Summary: PowerShell - Cross-platform automation and configuration tool/framework License: MIT URL: https://microsoft.com/powershell From 404cde8596a9cd1db5c1814758d93fb23ebcd841 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 19:17:01 +0000 Subject: [PATCH 19/26] Add Azure Linux (Mariner) support for RPM builds Include IsMariner check when determining if rpmbuild should be installed. This ensures RPM packaging works on Azure Linux (CBL-Mariner) systems. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- build.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.psm1 b/build.psm1 index 89c1506b8fa..78601a238fd 100644 --- a/build.psm1 +++ b/build.psm1 @@ -2412,7 +2412,7 @@ function Start-PSBootstrap { } # For RPM-based systems, ensure rpmbuild is available - if ($environment.IsLinux -and ($environment.IsRedHatFamily -or $environment.IsSUSEFamily)) { + if ($environment.IsLinux -and ($environment.IsRedHatFamily -or $environment.IsSUSEFamily -or $environment.IsMariner)) { Write-Verbose -Verbose "Checking for rpmbuild..." if (!(Get-Command rpmbuild -ErrorAction SilentlyContinue)) { Write-Warning "rpmbuild not found. Installing rpm-build package..." From 141912817c33afde81617c7ff02b3991b60e5f32 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 20:14:06 +0000 Subject: [PATCH 20/26] Disable binary stripping for cross-architecture RPM builds When building ARM64 RPMs on x86_64 hosts, the native /bin/strip tool cannot process ARM64 binaries and fails. Disable automatic binary stripping and debug package generation for cross-architecture builds by adding %define __strip /bin/true and %define debug_package %{nil} to the RPM spec file. This fixes the error: "/bin/strip: Unable to recognise the format of the input file" when building arm64 packages on x86_64. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 51161c3947c..7c2c6c55021 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1605,7 +1605,9 @@ AutoReq: no } else { # For cross-architecture builds, don't specify BuildArch in spec # The --target option will handle the architecture - $specContent += "`n" + # Also disable automatic binary stripping to avoid strip errors with cross-arch binaries + $specContent += "%define __strip /bin/true`n" + $specContent += "%define debug_package %{nil}`n`n" } # Add dependencies From 5b0e023010084470e41bfdbda7544580ab02758e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 20:22:04 +0000 Subject: [PATCH 21/26] Add detailed comments explaining cross-arch RPM build directives Added explanatory comments with documentation links for the RPM spec directives that disable binary stripping and debug packages for cross-architecture builds. This clarifies why these settings are necessary when building ARM64 RPMs on x86_64 hosts. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 7c2c6c55021..ed5ea6df0ed 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1605,8 +1605,16 @@ AutoReq: no } else { # For cross-architecture builds, don't specify BuildArch in spec # The --target option will handle the architecture - # Also disable automatic binary stripping to avoid strip errors with cross-arch binaries + + # 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" + # See: https://rpm-software-management.github.io/rpm/manual/macros.html $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 $specContent += "%define debug_package %{nil}`n`n" } From 6248d4b4b988234f03e0a16207e814afcc3edb25 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 20:25:28 +0000 Subject: [PATCH 22/26] Install fpm on Mariner for DEB package builds Added IsMariner check to fpm installation condition since DEB packages are also built on Azure Linux (Mariner) systems. This ensures fpm is available on all platforms where it's needed for package generation. Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> --- build.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.psm1 b/build.psm1 index 78601a238fd..88878bdd635 100644 --- a/build.psm1 +++ b/build.psm1 @@ -2403,8 +2403,8 @@ function Start-PSBootstrap { # Install [fpm](https://github.com/jordansissel/fpm) # Note: fpm is now only needed for DEB and macOS packages; RPM packages use rpmbuild directly if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') { - # Only install fpm on Debian-based systems and macOS - if (($environment.IsLinux -and $environment.IsDebianFamily) -or $environment.IsMacOS) { + # Install fpm on Debian-based systems, macOS, and Mariner (where DEB packages are built) + if (($environment.IsLinux -and ($environment.IsDebianFamily -or $environment.IsMariner)) -or $environment.IsMacOS) { Install-GlobalGem -Sudo $sudo -GemName "dotenv" -GemVersion "2.8.1" Install-GlobalGem -Sudo $sudo -GemName "ffi" -GemVersion "1.16.3" Install-GlobalGem -Sudo $sudo -GemName "fpm" -GemVersion "1.15.1" From a959d3989d02f17fe6b1c21837128fd40b42d9bd Mon Sep 17 00:00:00 2001 From: Travis Plunk Date: Mon, 20 Oct 2025 13:28:01 -0700 Subject: [PATCH 23/26] Apply suggestion from @TravisEz13 --- tools/packaging/packaging.psm1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index ed5ea6df0ed..a6da07d0772 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1610,6 +1610,8 @@ AutoReq: no # The native /bin/strip on x86_64 cannot process ARM64 binaries and would fail with: # "Unable to recognise the format of the input file" # See: https://rpm-software-management.github.io/rpm/manual/macros.html + # __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 From 2601b32e332a834937cd9f3211f939beaa801df3 Mon Sep 17 00:00:00 2001 From: Travis Plunk Date: Mon, 20 Oct 2025 13:32:50 -0700 Subject: [PATCH 24/26] Apply suggestion from @TravisEz13 --- tools/packaging/packaging.psm1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index a6da07d0772..8aaaa4aa39c 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1617,7 +1617,8 @@ AutoReq: no # 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 - $specContent += "%define debug_package %{nil}`n`n" + # See: https://docs.fedoraproject.org/en-US/packaging-guidelines/Debuginfo/#_useless_or_incomplete_debuginfo_packages_due_to_other_reasons + $specContent += "%global debug_package %{nil}`n`n" } # Add dependencies From 5289ca4d2dd5a84af48e9ff2981042e721de4f10 Mon Sep 17 00:00:00 2001 From: Travis Plunk Date: Mon, 20 Oct 2025 14:11:02 -0700 Subject: [PATCH 25/26] Update tools/packaging/packaging.psm1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tools/packaging/packaging.psm1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 8aaaa4aa39c..894323a4b98 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1656,6 +1656,7 @@ cp $ManGzipFile `$RPM_BUILD_ROOT$ManDestination $specContent += "mkdir -p `$RPM_BUILD_ROOT$linkDir`n" # For RPM, we copy the symlink itself (which fpm does by including it in the source) # The symlink at $link.Source points to the actual target, so we'll copy it + # The -P flag preserves symlinks rather than copying their targets, which is critical for this operation. $specContent += "cp -P $($link.Source) `$RPM_BUILD_ROOT$($link.Destination)`n" } From 3d796768e69bdf801e1b175604714161184c0706 Mon Sep 17 00:00:00 2001 From: Travis Plunk Date: Mon, 20 Oct 2025 14:12:39 -0700 Subject: [PATCH 26/26] Update test/packaging/linux/package-validation.tests.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../packaging/linux/package-validation.tests.ps1 | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/packaging/linux/package-validation.tests.ps1 b/test/packaging/linux/package-validation.tests.ps1 index 0c535447592..241863de45d 100644 --- a/test/packaging/linux/package-validation.tests.ps1 +++ b/test/packaging/linux/package-validation.tests.ps1 @@ -27,10 +27,20 @@ Describe "Linux Package Name Validation" { } $invalidPackages = @() + # Regex pattern for valid RPM package names. + # Breakdown: + # ^powershell\- : Starts with 'powershell-' + # (preview-|lts-)? : Optionally 'preview-' or 'lts-' + # \d+\.\d+\.\d+ : Version number (e.g., 7.6.0) + # (_[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.' + # (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) { - # Pattern matches: powershell-preview-7.6.0-1.preview.6.x86_64.rpm or powershell-7.6.0-1.x86_64.rpm - # Also matches: powershell-lts-7.6.0-1.rh.x86_64.rpm - if ($package.Name -notmatch 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(preview\.\d+\.)?(rh|cm)\.(x86_64|aarch64)\.rpm') { + if ($package.Name -notmatch $rpmPackageNamePattern) { $invalidPackages += "$($package.Name) is not a valid RPM package name" Write-Warning "$($package.Name) is not a valid RPM package name" }