diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e03903f9..9343e5bbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Remove Pester as a module include with the PowerShell Packages. In the future, you should be able to add it by running `Install-Module Pester`. (#5623, #5631) - Make Travis CI use `libcurl+openssl+gssapi` (#5629, @markekraus) +- Refactor `New-UnixPackaging` into functions to make the large function more readable. (#5625) ## v6.0.0-rc - 2017-11-16 diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index bbcd0f43a..60cd31787 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -582,28 +582,8 @@ function New-UnixPackage { } } - foreach ($Dependency in "fpm", "ronn") { - if (!(precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Package")) { - # These tools are not added to the path automatically on OpenSUSE 13.2 - # try adding them to the path and re-tesing first - [string] $gemsPath = $null - [string] $depenencyPath = $null - $gemsPath = Get-ChildItem -Path /usr/lib64/ruby/gems | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName - if($gemsPath) { - $depenencyPath = Get-ChildItem -Path (Join-Path -Path $gemsPath -ChildPath "gems" -AdditionalChildPath $Dependency) -Recurse | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty DirectoryName - $originalPath = $env:PATH - $env:PATH = $ENV:PATH +":" + $depenencyPath - if((precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Package")) { - continue - } - else { - $env:PATH = $originalPath - } - } - - throw "Dependency precheck failed!" - } - } + # Verify depenecies are installed and in the path + Test-Dependencies $Description = $packagingStrings.Description @@ -633,31 +613,14 @@ function New-UnixPackage { } elseif ($Environment.IsMacOS) { "/usr/local/bin" } + $linkSource = "/tmp/pwsh" if($pscmdlet.ShouldProcess("Create package file system")) { - New-Item -Force -ItemType SymbolicLink -Path "/tmp/pwsh" -Target "$Destination/pwsh" >$null - - if ($Environment.IsRedHatFamily) { - # add two symbolic links to system shared libraries that libmi.so is dependent on to handle - # platform specific changes. This is the only set of platforms needed for this currently - # as Ubuntu has these specific library files in the platform and macOS builds for itself - # against the correct versions. - New-Item -Force -ItemType SymbolicLink -Target "/lib64/libssl.so.10" -Path "$Staging/libssl.so.1.0.0" >$null - New-Item -Force -ItemType SymbolicLink -Target "/lib64/libcrypto.so.10" -Path "$Staging/libcrypto.so.1.0.0" >$null - - $AfterInstallScript = [io.path]::GetTempFileName() - $AfterRemoveScript = [io.path]::GetTempFileName() - $packagingStrings.RedHatAfterInstallScript -f "$Link/pwsh" | Out-File -FilePath $AfterInstallScript -Encoding ascii - $packagingStrings.RedHatAfterRemoveScript -f "$Link/pwsh" | Out-File -FilePath $AfterRemoveScript -Encoding ascii - } - elseif ($Environment.IsUbuntu -or $Environment.IsDebian) { - $AfterInstallScript = [io.path]::GetTempFileName() - $AfterRemoveScript = [io.path]::GetTempFileName() - $packagingStrings.UbuntuAfterInstallScript -f "$Link/pwsh" | Out-File -FilePath $AfterInstallScript -Encoding ascii - $packagingStrings.UbuntuAfterRemoveScript -f "$Link/pwsh" | Out-File -FilePath $AfterRemoveScript -Encoding ascii - } + New-Item -Force -ItemType SymbolicLink -Path $linkSource -Target "$Destination/pwsh" >$null + # Generate After Install and After Remove scripts + $AfterScriptInfo = New-AfterScripts # there is a weird bug in fpm # if the target of the powershell symlink exists, `fpm` aborts @@ -672,30 +635,259 @@ function New-UnixPackage { } } - # run ronn to convert man page to roff - $RonnFile = Join-Path $PSScriptRoot "/../../assets/pwsh.1.ronn" - $RoffFile = $RonnFile -replace "\.ronn$" - - # Run ronn on assets file - # Run does not play well with files named powershell6.0.1, so we generate and then rename - Start-NativeExecution { ronn --roff $RonnFile } - - # gzip in assets directory - $GzipFile = "$RoffFile.gz" - Start-NativeExecution { gzip -f $RoffFile } - - $ManFile = Join-Path "/usr/local/share/man/man1" (Split-Path -Leaf $GzipFile) + # Generate gzip of man file + $ManGzipInfo = New-ManGzip # Change permissions for packaging Start-NativeExecution { find $Staging -type d | xargs chmod 755 find $Staging -type f | xargs chmod 644 - chmod 644 $GzipFile + chmod 644 $ManGzipInfo.GzipFile chmod 755 "$Staging/pwsh" # only the executable should be executable } } + # Add macOS powershell launcher + if($Type -eq "osxpkg") + { + if($pscmdlet.ShouldProcess("Add macOS launch application")) + { + # Generate launcher app folder + $appsfolder = New-MacOSLauncher -Version $Version + $Arguments += "$appsfolder=/" + } + } + + $packageDependenciesParams = @{} + if($DebDistro) + { + $packageDependenciesParams['Distribution']=$DebDistro + } + # Setup package dependencies + $Dependencies = @(Get-PackageDependencies @packageDependenciesParams) + + $Arguments = Get-FpmArguments ` + -Name $Name ` + -Version $Version ` + -Iteration $Iteration ` + -Description $Description ` + -Type $Type ` + -Dependencies $Dependencies ` + -AfterInstallScript $AfterScriptInfo.AfterInstallScript ` + -AfterRemoveScript $AfterScriptInfo.AfterRemoveScript ` + -Staging $Staging ` + -Destination $Destination ` + -ManGzipFile $ManGzipInfo.GzipFile ` + -ManDestination $ManGzipInfo.ManFile ` + -LinkSource $LinkSource ` + -LinkDestination $Link + + # Build package + try { + if($pscmdlet.ShouldProcess("Create $type package")) { + $Output = Start-NativeExecution { fpm $Arguments } + } + } finally { + if ($Environment.IsMacOS) { + if($pscmdlet.ShouldProcess("Cleanup macOS launcher")) + { + Clear-MacOSLauncher + } + + # this is continuation of a fpm hack for a weird bug + if (Test-Path $hack_dest) { + Write-Warning "Move $hack_dest to $symlink_dest (fpm utime bug)" + Move-Item $hack_dest $symlink_dest + } + } + if ($AfterScriptInfo.AfterInstallScript) { + Remove-Item -erroraction 'silentlycontinue' $AfterScriptInfo.AfterInstallScript + } + if ($AfterScriptInfo.AfterRemoveScript) { + Remove-Item -erroraction 'silentlycontinue' $AfterScriptInfo.AfterRemoveScript + } + Remove-Item -Path $ManGzipInfo.GzipFile -Force -ErrorAction SilentlyContinue + } + + # Magic to get path output + $createdPackage = Get-Item (Join-Path $PWD (($Output[-1] -split ":path=>")[-1] -replace '["{}]')) + + if ($Environment.IsMacOS) { + if ($pscmdlet.ShouldProcess("Fix package name")) + { + # Add the OS information to the macOS package file name. + $packageExt = [System.IO.Path]::GetExtension($createdPackage.Name) + $packageNameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($createdPackage.Name) + + $newPackageName = "{0}-{1}{2}" -f $packageNameWithoutExt, $script:Options.Runtime, $packageExt + $newPackagePath = Join-Path $createdPackage.DirectoryName $newPackageName + + # -Force is not deleting the NewName if it exists, so delete it if it does + if ($Force -and (Test-Path -Path $newPackagePath)) + { + Remove-Item -Force $newPackagePath + } + + $createdPackage = Rename-Item -Path $createdPackage.FullName -NewName $newPackagePath -PassThru -ErrorAction Stop + } + } + + if (Test-Path $createdPackage) + { + Write-Verbose "Created package: $createdPackage" -Verbose + return $createdPackage + } + else + { + throw "Failed to create $createdPackage" + } + } +} + + +function Get-FpmArguments +{ + 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, + + # From start-PSPackage without modification, already validated + # Values: deb, rpm, osxpkg + [Parameter(Mandatory,HelpMessage='Installer Type')] + [String]$Type, + + [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')] + [String]$LinkSource, + + [Parameter(Mandatory,HelpMessage='Destination for symlink to powershell executable')] + [String]$LinkDestination, + + [Parameter(HelpMessage='Packages required to install this package. Not applicable for MacOS.')] + [ValidateScript({ + if (!$Environment.IsMacOS -and $_.Count -eq 0) + { + throw "Must not be null or empty on this environment." + } + return $true + })] + [String[]]$Dependencies, + + [Parameter(HelpMessage='Script to run after the package installation.')] + [AllowNull()] + [ValidateScript({ + if (!$Environment.IsMacOS -and !$_) + { + throw "Must not be null on this environment." + } + return $true + })] + [String]$AfterInstallScript, + + [Parameter(HelpMessage='Script to run after the package removal.')] + [AllowNull()] + [ValidateScript({ + if (!$Environment.IsMacOS -and !$_) + { + throw "Must not be null on this environment." + } + return $true + })] + [String]$AfterRemoveScript + ) + + $Arguments = @( + "--force", "--verbose", + "--name", $Name, + "--version", $Version, + "--iteration", $Iteration, + "--maintainer", "PowerShell Team ", + "--vendor", "Microsoft Corporation", + "--url", "https://microsoft.com/powershell", + "--license", "MIT License", + "--description", $Description, + "--category", "shells", + "-t", $Type, + "-s", "dir" + ) + if ($Environment.IsRedHatFamily) { + $Arguments += @("--rpm-dist", "rhel.7") + $Arguments += @("--rpm-os", "linux") + } + + if ($Environment.IsMacOS) { + $Arguments += @("--osxpkg-identifier-prefix", "com.microsoft") + } + + foreach ($Dependency in $Dependencies) { + $Arguments += @("--depends", $Dependency) + } + + if ($AfterInstallScript) { + $Arguments += @("--after-install", $AfterInstallScript) + } + + if ($AfterRemoveScript) { + $Arguments += @("--after-remove", $AfterRemoveScript) + } + + $Arguments += @( + "$Staging/=$Destination/", + "$ManGzipFile=$ManDestination", + "$LinkSource=$LinkDestination" + ) + + return $Arguments +} + +function Test-Distribution +{ + param( + [String] + $Distribution + ) + + if ( ($Environment.IsUbuntu -or $Environment.IsDebian) -and !$Distribution ) + { + throw "$Distribution is required for a Debian based distribution." + } + + if($Script:DebianDistributions -notcontains $Distribution) + { + throw "$Distribution should be one of the following: $Script:DebianDistributions" + } + return $true +} +function Get-PackageDependencies +{ + param( + [String] + [ValidateScript({Test-Distribution -Distribution $_})] + $Distribution + ) + + End { # These should match those in the Dockerfiles, but exclude tools like Git, which, and curl $Dependencies = @() if ($Environment.IsUbuntu -or $Environment.IsDebian) { @@ -711,13 +903,13 @@ function New-UnixPackage { "zlib1g" ) - switch ($DebDistro) { + switch ($Distribution) { "ubuntu.14.04" { $Dependencies += @("libssl1.0.0", "libicu52") } "ubuntu.16.04" { $Dependencies += @("libssl1.0.0", "libicu55") } "ubuntu.17.04" { $Dependencies += @("libssl1.0.0", "libicu57") } - "debian.8" { $Dependencies += @("libssl1.0.0", "libicu52") } - "debian.9" { $Dependencies += @("libssl1.0.2", "libicu57") } - default { throw "Debian distro '$DebDistro' is not supported." } + "debian.8" { $Dependencies += @("libssl1.0.0", "libicu52") } + "debian.9" { $Dependencies += @("libssl1.0.2", "libicu57") } + default { throw "Debian distro '$Distribution' is not supported." } } } elseif ($Environment.IsRedHatFamily) { $Dependencies = @( @@ -728,157 +920,154 @@ function New-UnixPackage { ) } - $Arguments = @( - "--force", "--verbose", - "--name", $Name, - "--version", $Version, - "--iteration", $Iteration, - "--maintainer", "PowerShell Team ", - "--vendor", "Microsoft Corporation", - "--url", "https://microsoft.com/powershell", - "--license", "MIT License", - "--description", $Description, - "--category", "shells", - "-t", $Type, - "-s", "dir" - ) - if ($Environment.IsRedHatFamily) { - $Arguments += @("--rpm-dist", "rhel.7") - $Arguments += @("--rpm-os", "linux") - } - if ($Environment.IsMacOS) { - $Arguments += @("--osxpkg-identifier-prefix", "com.microsoft") - } - foreach ($Dependency in $Dependencies) { - $Arguments += @("--depends", $Dependency) - } - if ($AfterInstallScript) { - $Arguments += @("--after-install", $AfterInstallScript) - } - if ($AfterRemoveScript) { - $Arguments += @("--after-remove", $AfterRemoveScript) - } - $Arguments += @( - "$Staging/=$Destination/", - "$GzipFile=$ManFile", - "/tmp/pwsh=$Link" - ) + return $Dependencies + } +} - # Add macOS powershell launcher - if($Type -eq "osxpkg") - { - if($pscmdlet.ShouldProcess("Add macOS launch application")) { - # Define folder for launch application. - $macosapp = "$PSScriptRoot/macos/launcher/ROOT/Applications/Powershell.app" - - # Update icns file. - $iconfile = "$PSScriptRoot/../../assets/Powershell.icns" - $iconfilebase = (Get-Item -Path $iconfile).BaseName - - # Create Resources folder, ignore error if exists. - New-Item -Force -ItemType Directory -Path "$macosapp/Contents/Resources" | Out-Null - Copy-Item -Force -Path $iconfile -Destination "$macosapp/Contents/Resources" - - # Set values in plist. - $plist = "$macosapp/Contents/Info.plist" - Start-NativeExecution { - defaults write $plist CFBundleIdentifier com.microsoft.powershell - defaults write $plist CFBundleVersion $Version - defaults write $plist CFBundleShortVersionString $Version - defaults write $plist CFBundleGetInfoString $Version - defaults write $plist CFBundleIconFile $iconfilebase +function Test-Dependencies +{ + foreach ($Dependency in "fpm", "ronn") { + if (!(precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Package")) { + # These tools are not added to the path automatically on OpenSUSE 13.2 + # try adding them to the path and re-tesing first + [string] $gemsPath = $null + [string] $depenencyPath = $null + $gemsPath = Get-ChildItem -Path /usr/lib64/ruby/gems | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName + if($gemsPath) { + $depenencyPath = Get-ChildItem -Path (Join-Path -Path $gemsPath -ChildPath "gems" -AdditionalChildPath $Dependency) -Recurse | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty DirectoryName + $originalPath = $env:PATH + $env:PATH = $ENV:PATH +":" + $depenencyPath + if((precheck $Dependency "Package dependency '$Dependency' not found. Run Start-PSBootstrap -Package")) { + continue } - - # Convert to XML plist, needed because defaults native - # app auto converts it to binary format when it modify - # the plist file. - Start-NativeExecution { - plutil -convert xml1 $plist - } - - # Set permissions for plist and shell script. Note that - # defaults native app sets 700 when writing to the plist - # file from above. Both of these will be reset post fpm. - $shellscript = "$macosapp/Contents/MacOS/PowerShell.sh" - Start-NativeExecution { - chmod 644 $plist - chmod 755 $shellscript - } - - # Add app folder to fpm paths. - $appsfolder = (Resolve-Path -Path "$macosapp/..").Path - $Arguments += "$appsfolder=/" - } - } - - # Build package - try { - if($pscmdlet.ShouldProcess("Create $type package")) { - $Output = Start-NativeExecution { fpm $Arguments } - } - } finally { - if ($Environment.IsMacOS) { - if($pscmdlet.ShouldProcess("Cleanup macOS launcher")) - { - # This is needed to prevent installer from picking up - # the launcher app in the build structure and updating - # it which locks out subsequent package builds due to - # increase permissions. - $macosapp = "$PSScriptRoot/macos/launcher/ROOT/Applications/Powershell.app" - $plist = "$macosapp/Contents/Info.plist" - $tempguid = (New-Guid).Guid - Start-NativeExecution { - defaults write $plist CFBundleIdentifier $tempguid - plutil -convert xml1 $plist - } - - # Restore default permissions. - $shellscript = "$macosapp/Contents/MacOS/PowerShell.sh" - Start-NativeExecution { - chmod 644 $shellscript - chmod 644 $plist - } - } - - # this is continuation of a fpm hack for a weird bug - if (Test-Path $hack_dest) { - Write-Warning "Move $hack_dest to $symlink_dest (fpm utime bug)" - Move-Item $hack_dest $symlink_dest + else { + $env:PATH = $originalPath } } - if ($AfterInstallScript) { - Remove-Item -erroraction 'silentlycontinue' $AfterInstallScript - } - if ($AfterRemoveScript) { - Remove-Item -erroraction 'silentlycontinue' $AfterRemoveScript - } - Remove-Item -Path $GzipFile -Force -ErrorAction SilentlyContinue - } - # Magic to get path output - $createdPackage = Get-Item (Join-Path $PWD (($Output[-1] -split ":path=>")[-1] -replace '["{}]')) - - if ($Environment.IsMacOS) { - if($pscmdlet.ShouldProcess("Fix package name")) - { - # Add the OS information to the macOS package file name. - $packageExt = [System.IO.Path]::GetExtension($createdPackage.Name) - $packageNameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($createdPackage.Name) - - $newPackageName = "{0}-{1}{2}" -f $packageNameWithoutExt, $script:Options.Runtime, $packageExt - $newPackagePath = Join-Path $createdPackage.DirectoryName $newPackageName - $createdPackage = Rename-Item $createdPackage.FullName $newPackagePath -PassThru -Force:$Force - } + throw "Dependency precheck failed!" } + } +} - if (Test-Path $createdPackage) - { - return $createdPackage - } - else - { - throw "Failed to create $createdPackage" - } +function New-AfterScripts +{ + if ($Environment.IsRedHatFamily) { + # add two symbolic links to system shared libraries that libmi.so is dependent on to handle + # platform specific changes. This is the only set of platforms needed for this currently + # as Ubuntu has these specific library files in the platform and macOS builds for itself + # against the correct versions. + New-Item -Force -ItemType SymbolicLink -Target "/lib64/libssl.so.10" -Path "$Staging/libssl.so.1.0.0" >$null + New-Item -Force -ItemType SymbolicLink -Target "/lib64/libcrypto.so.10" -Path "$Staging/libcrypto.so.1.0.0" >$null + + $AfterInstallScript = [io.path]::GetTempFileName() + $AfterRemoveScript = [io.path]::GetTempFileName() + $packagingStrings.RedHatAfterInstallScript -f "$Link/pwsh" | Out-File -FilePath $AfterInstallScript -Encoding ascii + $packagingStrings.RedHatAfterRemoveScript -f "$Link/pwsh" | Out-File -FilePath $AfterRemoveScript -Encoding ascii + } + elseif ($Environment.IsUbuntu -or $Environment.IsDebian) { + $AfterInstallScript = [io.path]::GetTempFileName() + $AfterRemoveScript = [io.path]::GetTempFileName() + $packagingStrings.UbuntuAfterInstallScript -f "$Link/pwsh" | Out-File -FilePath $AfterInstallScript -Encoding ascii + $packagingStrings.UbuntuAfterRemoveScript -f "$Link/pwsh" | Out-File -FilePath $AfterRemoveScript -Encoding ascii + } + + return [PSCustomObject] @{ + AfterInstallScript = $AfterInstallScript + AfterRemoveScript = $AfterRemoveScript + } +} + +function New-ManGzip +{ + # run ronn to convert man page to roff + $RonnFile = Join-Path $PSScriptRoot "/../../assets/pwsh.1.ronn" + $RoffFile = $RonnFile -replace "\.ronn$" + + # Run ronn on assets file + # Run does not play well with files named powershell6.0.1, so we generate and then rename + Start-NativeExecution { ronn --roff $RonnFile } + + # gzip in assets directory + $GzipFile = "$RoffFile.gz" + Start-NativeExecution { gzip -f $RoffFile } + + $ManFile = Join-Path "/usr/local/share/man/man1" (Split-Path -Leaf $GzipFile) + + return [PSCustomObject ] @{ + GZipFile = $GzipFile + ManFile = $ManFile + } +} +function New-MacOSLauncher +{ + param( + [Parameter(Mandatory)] + [String]$Version + ) + + # Define folder for launch application. + $macosapp = "$PSScriptRoot/macos/launcher/ROOT/Applications/Powershell.app" + + # Update icns file. + $iconfile = "$PSScriptRoot/../../assets/Powershell.icns" + $iconfilebase = (Get-Item -Path $iconfile).BaseName + + # Create Resources folder, ignore error if exists. + New-Item -Force -ItemType Directory -Path "$macosapp/Contents/Resources" | Out-Null + Copy-Item -Force -Path $iconfile -Destination "$macosapp/Contents/Resources" + + # Set values in plist. + $plist = "$macosapp/Contents/Info.plist" + Start-NativeExecution { + defaults write $plist CFBundleIdentifier com.microsoft.powershell + defaults write $plist CFBundleVersion $Version + defaults write $plist CFBundleShortVersionString $Version + defaults write $plist CFBundleGetInfoString $Version + defaults write $plist CFBundleIconFile $iconfilebase + } + + # Convert to XML plist, needed because defaults native + # app auto converts it to binary format when it modify + # the plist file. + Start-NativeExecution { + plutil -convert xml1 $plist + } + + # Set permissions for plist and shell script. Note that + # defaults native app sets 700 when writing to the plist + # file from above. Both of these will be reset post fpm. + $shellscript = "$macosapp/Contents/MacOS/PowerShell.sh" + Start-NativeExecution { + chmod 644 $plist + chmod 755 $shellscript + } + + # Add app folder to fpm paths. + $appsfolder = (Resolve-Path -Path "$macosapp/..").Path + + return $appsfolder +} + +function Clear-MacOSLauncher +{ + # This is needed to prevent installer from picking up + # the launcher app in the build structure and updating + # it which locks out subsequent package builds due to + # increase permissions. + $macosapp = "$PSScriptRoot/macos/launcher/ROOT/Applications/Powershell.app" + $plist = "$macosapp/Contents/Info.plist" + $tempguid = (New-Guid).Guid + Start-NativeExecution { + defaults write $plist CFBundleIdentifier $tempguid + plutil -convert xml1 $plist + } + + # Restore default permissions. + $shellscript = "$macosapp/Contents/MacOS/PowerShell.sh" + Start-NativeExecution { + chmod 644 $shellscript + chmod 644 $plist } }