Skip to content

Commit de7f155

Browse files
CopilotTravisEz13Copilot
committed
Replace fpm with native rpmbuild for RPM package generation (#26233)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: TravisEz13 <10873629+TravisEz13@users.noreply.github.com> Co-authored-by: Travis Plunk <travis.plunk@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent ea0bfb3 commit de7f155

File tree

6 files changed

+440
-40
lines changed

6 files changed

+440
-40
lines changed

.github/actions/test/linux-packaging/action.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ runs:
6262
# Create the artifacts staging directory
6363
New-Item -ItemType Directory -Path "$env:BUILD_ARTIFACTSTAGINGDIRECTORY" -Force | Out-Null
6464
65+
# Import packaging module to ensure RPM packaging changes are loaded
66+
Import-Module ./build.psm1 -Force
67+
Import-Module ./tools/packaging/packaging.psm1 -Force
6568
Import-Module ./tools/ci.psm1
6669
Restore-PSOptions -PSOptionsPath '${{ runner.workspace }}/build/psoptions.json'
6770
$options = (Get-PSOptions)
@@ -75,6 +78,16 @@ runs:
7578
Invoke-CIFinish
7679
shell: pwsh
7780

81+
- name: Validate Package Names
82+
run: |-
83+
# Run Pester tests to validate package names
84+
Import-Module Pester -Force
85+
$testResults = Invoke-Pester -Path ./test/packaging/linux/package-validation.tests.ps1 -PassThru
86+
if ($testResults.FailedCount -gt 0) {
87+
throw "Package validation tests failed"
88+
}
89+
shell: pwsh
90+
7891
- name: Upload deb packages
7992
uses: actions/upload-artifact@v4
8093
with:

.github/workflows/linux-ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ jobs:
5353
# Set job outputs to values from filter step
5454
outputs:
5555
source: ${{ steps.filter.outputs.source }}
56+
packagingChanged: ${{ steps.filter.outputs.packagingChanged }}
5657
steps:
5758
- name: checkout
5859
uses: actions/checkout@v5
@@ -239,7 +240,7 @@ jobs:
239240
needs:
240241
- ci_build
241242
- changes
242-
if: ${{ needs.changes.outputs.source == 'true' }}
243+
if: ${{ needs.changes.outputs.packagingChanged == 'true' }}
243244
runs-on: ubuntu-latest
244245
steps:
245246
- name: checkout

build.psm1

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,11 +2401,24 @@ function Start-PSBootstrap {
24012401
}
24022402

24032403
# Install [fpm](https://github.com/jordansissel/fpm)
2404+
# Note: fpm is now only needed for DEB and macOS packages; RPM packages use rpmbuild directly
24042405
if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') {
2405-
Install-GlobalGem -Sudo $sudo -GemName "dotenv" -GemVersion "2.8.1"
2406-
Install-GlobalGem -Sudo $sudo -GemName "ffi" -GemVersion "1.16.3"
2407-
Install-GlobalGem -Sudo $sudo -GemName "fpm" -GemVersion "1.15.1"
2408-
Install-GlobalGem -Sudo $sudo -GemName "rexml" -GemVersion "3.2.5"
2406+
# Install fpm on Debian-based systems, macOS, and Mariner (where DEB packages are built)
2407+
if (($environment.IsLinux -and ($environment.IsDebianFamily -or $environment.IsMariner)) -or $environment.IsMacOS) {
2408+
Install-GlobalGem -Sudo $sudo -GemName "dotenv" -GemVersion "2.8.1"
2409+
Install-GlobalGem -Sudo $sudo -GemName "ffi" -GemVersion "1.16.3"
2410+
Install-GlobalGem -Sudo $sudo -GemName "fpm" -GemVersion "1.15.1"
2411+
Install-GlobalGem -Sudo $sudo -GemName "rexml" -GemVersion "3.2.5"
2412+
}
2413+
2414+
# For RPM-based systems, ensure rpmbuild is available
2415+
if ($environment.IsLinux -and ($environment.IsRedHatFamily -or $environment.IsSUSEFamily -or $environment.IsMariner)) {
2416+
Write-Verbose -Verbose "Checking for rpmbuild..."
2417+
if (!(Get-Command rpmbuild -ErrorAction SilentlyContinue)) {
2418+
Write-Warning "rpmbuild not found. Installing rpm-build package..."
2419+
Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo $PackageManager install -y rpm-build")) -IgnoreExitcode
2420+
}
2421+
}
24092422
}
24102423
}
24112424

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
Describe "Linux Package Name Validation" {
5+
BeforeAll {
6+
# Determine artifacts directory (GitHub Actions or Azure DevOps)
7+
$artifactsDir = if ($env:GITHUB_ACTIONS -eq 'true') {
8+
"$env:GITHUB_WORKSPACE/../packages"
9+
} else {
10+
$env:SYSTEM_ARTIFACTSDIRECTORY
11+
}
12+
13+
if (-not $artifactsDir) {
14+
throw "Artifacts directory not found. GITHUB_WORKSPACE or SYSTEM_ARTIFACTSDIRECTORY must be set."
15+
}
16+
17+
Write-Verbose "Artifacts directory: $artifactsDir" -Verbose
18+
}
19+
20+
Context "RPM Package Names" {
21+
It "Should have valid RPM package names" {
22+
$rpmPackages = Get-ChildItem -Path $artifactsDir -Recurse -Filter *.rpm -ErrorAction SilentlyContinue
23+
24+
if ($rpmPackages.Count -eq 0) {
25+
Set-ItResult -Skipped -Because "No RPM packages found in artifacts directory"
26+
return
27+
}
28+
29+
$invalidPackages = @()
30+
# Regex pattern for valid RPM package names.
31+
# Breakdown:
32+
# ^powershell\- : Starts with 'powershell-'
33+
# (preview-|lts-)? : Optionally 'preview-' or 'lts-'
34+
# \d+\.\d+\.\d+ : Version number (e.g., 7.6.0)
35+
# (_[a-z]*\.\d+)? : Optional underscore, letters, dot, and digits (e.g., _alpha.1)
36+
# -1\. : Literal '-1.'
37+
# (preview\.\d+\.)? : Optional 'preview.' and digits, followed by a dot
38+
# (rh|cm)\. : Either 'rh.' or 'cm.'
39+
# (x86_64|aarch64)\.rpm$ : Architecture and file extension
40+
$rpmPackageNamePattern = 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1\.(preview\.\d+\.)?(rh|cm)\.(x86_64|aarch64)\.rpm'
41+
42+
foreach ($package in $rpmPackages) {
43+
if ($package.Name -notmatch $rpmPackageNamePattern) {
44+
$invalidPackages += "$($package.Name) is not a valid RPM package name"
45+
Write-Warning "$($package.Name) is not a valid RPM package name"
46+
}
47+
}
48+
49+
if ($invalidPackages.Count -gt 0) {
50+
throw ($invalidPackages | Out-String)
51+
}
52+
53+
$rpmPackages.Count | Should -BeGreaterThan 0
54+
}
55+
}
56+
57+
Context "Tar.Gz Package Names" {
58+
It "Should have valid tar.gz package names" {
59+
$tarPackages = Get-ChildItem -Path $artifactsDir -Recurse -Filter *.tar.gz -ErrorAction SilentlyContinue
60+
61+
if ($tarPackages.Count -eq 0) {
62+
Set-ItResult -Skipped -Because "No tar.gz packages found in artifacts directory"
63+
return
64+
}
65+
66+
$invalidPackages = @()
67+
foreach ($package in $tarPackages) {
68+
# Pattern matches: powershell-7.6.0-preview.6-linux-x64.tar.gz or powershell-7.6.0-linux-x64.tar.gz
69+
# Also matches various runtime configurations
70+
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)') {
71+
$invalidPackages += "$($package.Name) is not a valid tar.gz package name"
72+
Write-Warning "$($package.Name) is not a valid tar.gz package name"
73+
}
74+
}
75+
76+
if ($invalidPackages.Count -gt 0) {
77+
throw ($invalidPackages | Out-String)
78+
}
79+
80+
$tarPackages.Count | Should -BeGreaterThan 0
81+
}
82+
}
83+
84+
Context "Package Existence" {
85+
It "Should find at least one package in artifacts directory" {
86+
$allPackages = Get-ChildItem -Path $artifactsDir -Recurse -Include *.rpm, *.tar.gz, *.deb -ErrorAction SilentlyContinue
87+
88+
$allPackages.Count | Should -BeGreaterThan 0 -Because "At least one package should exist in the artifacts directory"
89+
}
90+
}
91+
}

tools/ci.psm1

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -874,16 +874,36 @@ function New-LinuxPackage
874874
$packageObj = $package
875875
}
876876

877-
Write-Log -message "Artifacts directory: ${env:BUILD_ARTIFACTSTAGINGDIRECTORY}"
878-
Copy-Item $packageObj.FullName -Destination "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" -Force
877+
# Determine artifacts directory (GitHub Actions or Azure DevOps)
878+
$artifactsDir = if ($env:GITHUB_ACTIONS -eq 'true') {
879+
"${env:GITHUB_WORKSPACE}/../packages"
880+
} else {
881+
"${env:BUILD_ARTIFACTSTAGINGDIRECTORY}"
882+
}
883+
884+
# Ensure artifacts directory exists
885+
if (-not (Test-Path $artifactsDir)) {
886+
New-Item -ItemType Directory -Path $artifactsDir -Force | Out-Null
887+
}
888+
889+
Write-Log -message "Artifacts directory: $artifactsDir"
890+
Copy-Item $packageObj.FullName -Destination $artifactsDir -Force
879891
}
880892

881893
if ($IsLinux)
882894
{
895+
# Determine artifacts directory (GitHub Actions or Azure DevOps)
896+
$artifactsDir = if ($env:GITHUB_ACTIONS -eq 'true') {
897+
"${env:GITHUB_WORKSPACE}/../packages"
898+
} else {
899+
"${env:BUILD_ARTIFACTSTAGINGDIRECTORY}"
900+
}
901+
883902
# Create and package Raspbian .tgz
903+
# Build must be clean for Raspbian
884904
Start-PSBuild -PSModuleRestore -Clean -Runtime linux-arm -Configuration 'Release'
885905
$armPackage = Start-PSPackage @packageParams -Type tar-arm -SkipReleaseChecks
886-
Copy-Item $armPackage -Destination "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}" -Force
906+
Copy-Item $armPackage -Destination $artifactsDir -Force
887907
}
888908
}
889909

0 commit comments

Comments
 (0)