Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5e6a80c
Initial plan
Copilot Oct 22, 2025
1ea792d
Add native DEB package builder and remove fpm dependency
Copilot Oct 22, 2025
f7fd1ba
Fix New-NativeDeb function: handle symlinks and control file format
Copilot Oct 22, 2025
85432aa
Add documentation comment to Get-FpmArguments function
Copilot Oct 22, 2025
c2f21df
Address code review feedback: improve control file generation, symlin…
Copilot Oct 22, 2025
7e03559
Address review feedback: use PowerShell commands directly, add dpkg f…
Copilot Oct 27, 2025
f7b1d5c
Install azurelinux-repos-extended before dpkg on Mariner
Copilot Oct 27, 2025
a191190
Merge branch 'master' into copilot/remove-fpm-deb-packaging
daxian-dbw Oct 27, 2025
cc0cfb0
Minor change to trigger CIs
daxian-dbw Oct 28, 2025
d91e9c7
Merge branch 'master' into copilot/remove-fpm-deb-packaging
daxian-dbw Oct 28, 2025
0816e95
Fix DEB packaging errors: control file format, symlink permissions, a…
Copilot Oct 28, 2025
d4b53b5
Simplify code: remove unnecessary else block and improve cp commands …
Copilot Oct 28, 2025
96956af
Fix DEB package name validation: update regex pattern and fail when n…
Copilot Oct 28, 2025
2c0891a
Make RPM and Tar.Gz package validation tests fail when no packages found
Copilot Oct 28, 2025
3bf1cfe
Apply suggestion from @daxian-dbw
daxian-dbw Oct 28, 2025
f514124
Merge branch 'master' into copilot/remove-fpm-deb-packaging
daxian-dbw Oct 28, 2025
98d973e
Use Write-LogGroup function for DEB control file output
Copilot Oct 28, 2025
0bba464
Merge branch 'master' into copilot/remove-fpm-deb-packaging
TravisEz13 Oct 28, 2025
74f4969
Address Copilot's comments
daxian-dbw Oct 28, 2025
1c55a66
Wrap native commands with Start-NativeExecution for proper error hand…
Copilot Oct 28, 2025
75c7795
Use -VerboseOutputOnError switch instead of manual error handling for…
Copilot Oct 28, 2025
97a7c38
Remove fpm installation code - no longer needed after native packagin…
Copilot Oct 28, 2025
089faae
Clean up fpm related code/comments in packaging.psm1
daxian-dbw Oct 28, 2025
95088c5
Remove Install-GlobalGem
daxian-dbw Oct 28, 2025
32d0f53
Remove a comment about fpm
daxian-dbw Oct 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions build.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -2316,7 +2316,9 @@ function Start-PSBootstrap {
elseif ($environment.IsUbuntu18) { $Deps += "libicu60"}

# Packaging tools
if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') { $Deps += "ruby-dev", "groff", "libffi-dev", "rpm", "g++", "make" }
# Note: ruby-dev, libffi-dev, g++, and make are no longer needed for DEB packaging
# DEB packages now use native dpkg-deb (pre-installed)
if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') { $Deps += "groff", "rpm" }

# Install dependencies
# change the fontend from apt-get to noninteractive
Expand All @@ -2340,7 +2342,9 @@ function Start-PSBootstrap {
$Deps += "libicu", "openssl-libs"

# Packaging tools
if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') { $Deps += "ruby-devel", "rpm-build", "groff", 'libffi-devel', "gcc-c++" }
# Note: ruby-devel and libffi-devel are no longer needed
# RPM packages use rpmbuild, DEB packages use dpkg-deb
if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') { $Deps += "rpm-build", "groff" }

$PackageManager = Get-RedHatPackageManager

Expand All @@ -2361,7 +2365,8 @@ function Start-PSBootstrap {
$Deps += "wget"

# Packaging tools
if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') { $Deps += "ruby-devel", "rpmbuild", "groff", 'libffi-devel', "gcc" }
# Note: ruby-devel and libffi-devel are no longer needed for packaging
if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') { $Deps += "rpmbuild", "groff" }

$PackageManager = "zypper --non-interactive install"
$baseCommand = "$sudo $PackageManager"
Expand Down Expand Up @@ -2401,10 +2406,10 @@ 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
# Note: fpm is now only needed for macOS packages; RPM and DEB packages use native builders
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
if ($Scenario -eq 'Both' -or $Scenario -eq 'Package') {
# 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 fpm only on macOS
if ($environment.IsMacOS) {
Install-GlobalGem -Sudo $sudo -GemName "dotenv" -GemVersion "2.8.1"
Comment thread
daxian-dbw marked this conversation as resolved.
Install-GlobalGem -Sudo $sudo -GemName "ffi" -GemVersion "1.16.3"
Install-GlobalGem -Sudo $sudo -GemName "fpm" -GemVersion "1.15.1"
Expand All @@ -2419,6 +2424,15 @@ function Start-PSBootstrap {
Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo $PackageManager install -y rpm-build")) -IgnoreExitcode
}
}

# For Debian-based systems, ensure dpkg-deb is available (usually pre-installed)
if ($environment.IsLinux -and $environment.IsDebianFamily) {
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
Write-Verbose -Verbose "Checking for dpkg-deb..."
if (!(Get-Command dpkg-deb -ErrorAction SilentlyContinue)) {
Write-Warning "dpkg-deb not found. Installing dpkg package..."
Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo apt-get install -y dpkg")) -IgnoreExitcode
}
}
}
}

Expand Down
245 changes: 238 additions & 7 deletions tools/packaging/packaging.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -1329,8 +1329,37 @@ function New-UnixPackage {
throw
}
}
} elseif ($Type -eq 'deb') {
# Use native DEB package builder
if ($PSCmdlet.ShouldProcess("Create DEB package natively")) {
Write-Log "Creating DEB package natively..."
try {
$result = New-NativeDeb `
-Name $Name `
-Version $packageVersion `
-Iteration $Iteration `
-Description $Description `
-Staging $Staging `
-Destination $Destination `
-ManGzipFile $ManGzipInfo.GzipFile `
-ManDestination $ManGzipInfo.ManFile `
-LinkInfo $Links `
-Dependencies $Dependencies `
-AfterInstallScript $AfterScriptInfo.AfterInstallScript `
-AfterRemoveScript $AfterScriptInfo.AfterRemoveScript `
-HostArchitecture $HostArchitecture `
-CurrentLocation $CurrentLocation

$Output = @("Created package {:path=>""$($result.PackageName)""}")
Comment thread
daxian-dbw marked this conversation as resolved.
}
catch {
Write-Verbose -Message "!!!Handling error in native DEB creation!!!" -Verbose -ErrorAction SilentlyContinue
Get-Error -InputObject $_
throw
}
}
} else {
# Use fpm for DEB and macOS packages
# Use fpm for macOS packages
$Arguments = @()

$Arguments += Get-FpmArguments `
Comment thread
daxian-dbw marked this conversation as resolved.
Expand Down Expand Up @@ -1692,6 +1721,205 @@ cp $ManGzipFile `$RPM_BUILD_ROOT$ManDestination
return $specContent
}

function New-NativeDeb
{
param(
[Parameter(Mandatory,HelpMessage='Package Name')]
[String]$Name,

[Parameter(Mandatory,HelpMessage='Package Version')]
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
[String]$Version,

[Parameter(Mandatory)]
[String]$Iteration,

[Parameter(Mandatory,HelpMessage='Package description')]
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
[String]$Description,

[Parameter(Mandatory,HelpMessage='Staging folder for installation files')]
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
[String]$Staging,

[Parameter(Mandatory,HelpMessage='Install path on target machine')]
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
[String]$Destination,

[Parameter(Mandatory,HelpMessage='The built and gzipped man file.')]
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
[String]$ManGzipFile,

[Parameter(Mandatory,HelpMessage='The destination of the man file')]
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
[String]$ManDestination,

[Parameter(Mandatory,HelpMessage='Symlink to powershell executable')]
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
[LinkInfo[]]$LinkInfo,

[Parameter(HelpMessage='Packages required to install this package.')]
[String[]]$Dependencies,

[Parameter(HelpMessage='Script to run after the package installation.')]
[String]$AfterInstallScript,

[Parameter(HelpMessage='Script to run after the package removal.')]
[String]$AfterRemoveScript,

[string]$HostArchitecture,

[string]$CurrentLocation
)

Write-Log "Creating native DEB package..."

# Create temporary build directory
$debBuildRoot = Join-Path $env:HOME "debbuild-$(Get-Random)"
$debianDir = Join-Path $debBuildRoot "DEBIAN"
$dataDir = Join-Path $debBuildRoot "data"

try {
New-Item -ItemType Directory -Path $debianDir -Force | Out-Null
New-Item -ItemType Directory -Path $dataDir -Force | Out-Null

# Calculate installed size (in KB)
$installedSize = 0
Get-ChildItem -Path $Staging -Recurse -File | ForEach-Object { $installedSize += $_.Length }
$installedSize += (Get-Item $ManGzipFile).Length
$installedSizeKB = [Math]::Ceiling($installedSize / 1024)

# Create control file with all fields in proper order
$controlContent = @"
Package: $Name
Version: $Version-$Iteration
Architecture: $HostArchitecture
Maintainer: PowerShell Team <PowerShellTeam@hotmail.com>
Installed-Size: $installedSizeKB
Priority: optional
Section: shells
Homepage: https://microsoft.com/powershell
Depends: $(if ($Dependencies) { $Dependencies -join ', ' })
Description: $Description

"@

$controlFile = Join-Path $debianDir "control"
$controlContent | Out-File -FilePath $controlFile -Encoding ascii -NoNewline

Write-Verbose "Control file created: $controlFile" -Verbose
if ($env:GITHUB_ACTIONS -eq 'true') {
Write-Host "::group::DEB Control File Content"
Write-Host $controlContent
Write-Host "::endgroup::"
}
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated

# Copy postinst script if provided
if ($AfterInstallScript -and (Test-Path $AfterInstallScript)) {
$postinstFile = Join-Path $debianDir "postinst"
Copy-Item -Path $AfterInstallScript -Destination $postinstFile -Force
chmod 755 $postinstFile
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
Write-Verbose "Postinst script copied to: $postinstFile" -Verbose
}

# Copy postrm script if provided
if ($AfterRemoveScript -and (Test-Path $AfterRemoveScript)) {
$postrmFile = Join-Path $debianDir "postrm"
Copy-Item -Path $AfterRemoveScript -Destination $postrmFile -Force
chmod 755 $postrmFile
Write-Verbose "Postrm script copied to: $postrmFile" -Verbose
}

# Copy staging files to data directory
$targetPath = Join-Path $dataDir $Destination.TrimStart('/')
New-Item -ItemType Directory -Path $targetPath -Force | Out-Null
Copy-Item -Path "$Staging/*" -Destination $targetPath -Recurse -Force
Write-Verbose "Copied staging files to: $targetPath" -Verbose

# Copy man page
$manDestPath = Join-Path $dataDir $ManDestination.TrimStart('/')
$manDestDir = Split-Path $manDestPath -Parent
New-Item -ItemType Directory -Path $manDestDir -Force | Out-Null
Copy-Item -Path $ManGzipFile -Destination $manDestPath -Force
Write-Verbose "Copied man page to: $manDestPath" -Verbose

# Copy symlinks from temporary locations
foreach ($link in $LinkInfo) {
$linkPath = Join-Path $dataDir $link.Destination.TrimStart('/')
$linkDir = Split-Path $linkPath -Parent
New-Item -ItemType Directory -Path $linkDir -Force | Out-Null

# Copy the temporary symlink file that was created by New-LinkInfo
# The Source contains a temporary symlink that points to the correct target
if (Test-Path $link.Source) {
# Use bash cp to preserve the symlink
bash -c "cp -P '$($link.Source)' '$linkPath'"
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
Write-Verbose "Copied symlink: $linkPath (from $($link.Source))" -Verbose
} else {
Write-Warning "Symlink source not found: $($link.Source)"
}
}

# Set proper permissions
Write-Verbose "Setting file permissions..." -Verbose
bash -c "find '$dataDir' -type d -exec chmod 755 '{}' \;"
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
bash -c "find '$dataDir' -type f -exec chmod 644 '{}' \;"

# Set executable permission for pwsh if it exists
$pwshPath = "$targetPath/pwsh"
if (Test-Path $pwshPath) {
chmod 755 "$pwshPath"
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
}

# Calculate md5sums for all files in data directory (excluding symlinks)
$md5sumsFile = Join-Path $debianDir "md5sums"
$md5Content = ""
Get-ChildItem -Path $dataDir -Recurse -File | Where-Object { -not $_.Target } | ForEach-Object {
$relativePath = $_.FullName.Substring($dataDir.Length + 1)
$md5Hash = (Get-FileHash -Path $_.FullName -Algorithm MD5).Hash.ToLower()
$md5Content += "$md5Hash $relativePath`n"
}
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
$md5Content | Out-File -FilePath $md5sumsFile -Encoding ascii -NoNewline
Write-Verbose "MD5 sums file created: $md5sumsFile" -Verbose

# Build the package using dpkg-deb
$debFileName = "${Name}_${Version}-${Iteration}_${HostArchitecture}.deb"
$debFilePath = Join-Path $CurrentLocation $debFileName

Write-Verbose "Building DEB package: $debFileName" -Verbose

# Copy DEBIAN directory and data files to build root
$buildDir = Join-Path $debBuildRoot "build"
New-Item -ItemType Directory -Path $buildDir -Force | Out-Null

# Use bash cp to preserve symlinks
bash -c "cp -a '$debianDir' '$buildDir/DEBIAN'"
bash -c "cp -a '$dataDir'/* '$buildDir/'"
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated

# Build package with dpkg-deb
$dpkgOutput = bash -c "dpkg-deb --build '$buildDir' '$debFilePath' 2>&1"
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
$exitCode = $LASTEXITCODE

if ($exitCode -ne 0) {
Write-Verbose "dpkg-deb output: $dpkgOutput" -Verbose
throw "dpkg-deb failed with exit code $exitCode"
}

if (Test-Path $debFilePath) {
Write-Log "Successfully created DEB package: $debFileName"
return @{
PackagePath = $debFilePath
PackageName = $debFileName
}
} else {
throw "DEB package file not found after build: $debFilePath"
}
}
finally {
# Cleanup temporary directory
if (Test-Path $debBuildRoot) {
Write-Verbose "Cleaning up temporary build directory: $debBuildRoot" -Verbose
Remove-Item -Path $debBuildRoot -Recurse -Force -ErrorAction SilentlyContinue
}
}
}

# Get-FpmArguments builds the argument list for the fpm tool.
# Note: This function is now only used for macOS package creation.
# DEB packages use New-NativeDeb, and RPM packages use rpmbuild directly with spec files.
function Get-FpmArguments
{
param(
Expand Down Expand Up @@ -1904,14 +2132,17 @@ function Get-PackageDependencies

function Test-Dependencies
{
# Note: RPM packages no longer require fpm; they use rpmbuild directly
# DEB packages still use fpm
# Test-Dependencies checks for required packaging tools.
# Note: RPM packages use rpmbuild directly (checked in build.psm1)
# DEB packages use native tools (dpkg-deb, ar, tar, gzip, md5sum) which are pre-installed
# macOS packages still use fpm (installed via build.psm1)
#
# This function is maintained for potential future dependency checks.
$Dependencies = @()

# Only check for fpm on Debian-based systems
if ($Environment.IsDebianFamily) {
$Dependencies += "fpm"
}
# No longer checking for fpm on Debian-based systems
# Native DEB packaging only requires standard tools: dpkg-deb, ar, tar, gzip, md5sum
# which are typically pre-installed on Debian-based systems
Comment thread
daxian-dbw marked this conversation as resolved.
Outdated

Comment thread
daxian-dbw marked this conversation as resolved.
Outdated
foreach ($Dependency in $Dependencies) {
if (!(precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Scenario Package")) {
Expand Down
Loading