Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of the Dynamic Support Policy for PowerShell Versions #1275 #1276

Merged
merged 9 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
30 changes: 30 additions & 0 deletions docs/pwshsupportpolicy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Pode PowerShell Version Support Policy
mdaneri marked this conversation as resolved.
Show resolved Hide resolved

## Overview
This document details the support policy for PowerShell versions as they relate to Pode releases. Our aim is to provide clarity on which versions of PowerShell are supported by each release of Pode, ensuring a secure, efficient, and compatible development environment.

## Policy Statement
Pode commits to supporting PowerShell versions that are not end of life (EOL) at the moment of each Pode release. This dynamic approach allows Pode to adapt to the evolving PowerShell ecosystem, ensuring compatibility with recent and supported PowerShell versions while maintaining a high security standard.

### Support Lifecycle
- For each Pode release, support is extended to versions of PowerShell that are not EOL at the time of release.
- Subsequent Pode releases may not support previously compatible PowerShell versions if those versions have reached EOL in the interim.
- This policy applies to all versions of PowerShell, including PowerShell (version 7.x), PowerShell Core (versions 6.x) and Windows PowerShell 5.1.
- Windows Powershell 5.1 will be supported by any Pode version released prior the Jan 7 2027

### Example
- **Pode 2.10 Release (April 2024)**: Supports PowerShell 7.2, 7.3, 7.4, and Windows PowerShell 5.1, assuming none are EOL at the time of release.
- **Pode 2.11 Release (Late 2024)**: With PowerShell 7.2 and 7.3 reaching EOL, support for these versions would be discontinued. Pode 2.11 would then support PowerShell Core 7.4 and any newer, non-EOL versions, along with Windows PowerShell 5.1.

## Testing Strategy
Pode is tested against the PowerShell versions it supports at the time of each release. Testing focuses on ensuring compatibility with non-EOL versions of PowerShell, reflecting the policy stated above.

## Warning Mechanism
A warning mechanism within Pode detects the PowerShell version in use at runtime. If a version no longer supported by the current release of Pode is detected—due to reaching EOL—a warning will be issued, advising on the potential risks and recommending an update to a supported version.

## Version Updates and Communication
Updates to the PowerShell version support policy will be documented in Pode’s release notes and official documentation. Users are encouraged to review these sources to stay informed about which PowerShell versions are supported by their version of Pode.

## Feedback and Contributions
Feedback on Pode's PowerShell version support policy is invaluable. We welcome suggestions and contributions from our community to help refine and improve our approach. Please share your thoughts through our GitHub repository or community forums.

2 changes: 1 addition & 1 deletion examples/web-auth-apikey-jwt.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Import-Module "$($path)/src/Pode.psm1" -Force -ErrorAction Stop
Start-PodeServer -Threads 2 {

# listen on localhost:8085
Add-PodeEndpoint -Address * -Port 8085 -Protocol Http
Add-PodeEndpoint -Address localhost -Port 8085 -Protocol Http

New-PodeLoggingMethod -File -Name 'requests' | Enable-PodeRequestLogging
New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging
Expand Down
28 changes: 16 additions & 12 deletions pode.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,6 @@ function Invoke-PodeBuildDotnetBuild($target ) {
else {
$AssemblyVersion = ''
}
dotnet build --configuration Release --self-contained --framework $target $AssemblyVersion
if (!$?) {
throw "dotnet build failed for $($target)"
}

dotnet publish --configuration Release --self-contained --framework $target $AssemblyVersion --output ../Libs/$target
if (!$?) {
Expand All @@ -130,14 +126,22 @@ function Invoke-PodeBuildDotnetBuild($target ) {

}

function Get-PodeBuildPwshEOL {
$eol = invoke-restmethod -Uri 'https://endoflife.date/api/powershell.json' -Headers @{Accept = 'application/json' }
return @{
eol = ($eol | Where-Object { [datetime]$_.eol -lt [datetime]::Now }).cycle -join ','
supported = ($eol | Where-Object { [datetime]$_.eol -ge [datetime]::Now }).cycle -join ','
}
}

<#
# Helper Tasks
#>

# Synopsis: Stamps the version onto the Module
Task StampVersion {
(Get-Content ./pkg/Pode.psd1) | ForEach-Object { $_ -replace '\$version\$', $Version } | Set-Content ./pkg/Pode.psd1
$pwshVersions = Get-PodeBuildPwshEOL
(Get-Content ./pkg/Pode.psd1) | ForEach-Object { $_ -replace '\$version\$', $Version -replace '\$versionUntested\$', $pwshVersions.eol -replace '\$versionSupported\$', $pwshVersions.supported -replace '\$buildyear\$', ((get-date).Year) } | Set-Content ./pkg/Pode.psd1
(Get-Content ./pkg/Pode.Internal.psd1) | ForEach-Object { $_ -replace '\$version\$', $Version } | Set-Content ./pkg/Pode.Internal.psd1
(Get-Content ./packers/choco/pode_template.nuspec) | ForEach-Object { $_ -replace '\$version\$', $Version } | Set-Content ./packers/choco/pode.nuspec
(Get-Content ./packers/choco/tools/ChocolateyInstall_template.ps1) | ForEach-Object { $_ -replace '\$version\$', $Version } | Set-Content ./packers/choco/tools/ChocolateyInstall.ps1
Expand Down Expand Up @@ -434,13 +438,13 @@ Task DocsBuild DocsDeps, DocsHelpBuild, {
}

# Synopsis: Clean the build enviroment
Task Clean CleanPkg,CleanDeliverable,CleanLibs,CleanListener
Task Clean CleanPkg, CleanDeliverable, CleanLibs, CleanListener

# Synopsis: Clean the Deliverable folder
Task CleanDeliverable {
$path = './deliverable'
if (Test-Path -Path $path -PathType Container) {
Write-Host "Removing ./deliverable folder"
Write-Host 'Removing ./deliverable folder'
Remove-Item -Path $path -Recurse -Force | Out-Null
}
Write-Host "Cleanup $path done"
Expand All @@ -450,16 +454,16 @@ Task CleanDeliverable {
Task CleanPkg {
$path = './pkg'
if ((Test-Path -Path $path -PathType Container )) {
Write-Host "Removing ./pkg folder"
Write-Host 'Removing ./pkg folder'
Remove-Item -Path $path -Recurse -Force | Out-Null
}

if ((Test-Path -Path .\packers\choco\tools\ChocolateyInstall.ps1 -PathType Leaf )) {
Write-Host "Removing .\packers\choco\tools\ChocolateyInstall.ps1"
Write-Host 'Removing .\packers\choco\tools\ChocolateyInstall.ps1'
Remove-Item -Path .\packers\choco\tools\ChocolateyInstall.ps1
}
if ((Test-Path -Path .\packers\choco\pode.nuspec -PathType Leaf )) {
Write-Host "Removing .\packers\choco\pode.nuspec"
Write-Host 'Removing .\packers\choco\pode.nuspec'
Remove-Item -Path .\packers\choco\pode.nuspec
}
Write-Host "Cleanup $path done"
Expand All @@ -470,7 +474,7 @@ Task CleanLibs {
$path = './src/Libs'
if (Test-Path -Path $path -PathType Container) {
Write-Host "Removing $path contents"
Remove-Item -Path $path -Recurse -Force | Out-Null
Remove-Item -Path $path -Recurse -Force | Out-Null
}
Write-Host "Cleanup $path done"
}
Expand All @@ -480,7 +484,7 @@ Task CleanListener {
$path = './src/Listener/bin'
if (Test-Path -Path $path -PathType Container) {
Write-Host "Removing $path contents"
Remove-Item -Path $path -Recurse -Force | Out-Null
Remove-Item -Path $path -Recurse -Force | Out-Null
}
Write-Host "Cleanup $path done"
}
Expand Down
21 changes: 13 additions & 8 deletions src/Pode.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
Author = 'Matthew Kelly (Badgerati)'

# Copyright statement for this module
Copyright = 'Copyright (c) 2017-2023 Matthew Kelly (Badgerati), licensed under the MIT License.'
Copyright = 'Copyright (c) 2017-$buildyear$ Matthew Kelly (Badgerati), licensed under the MIT License.'

# Description of the functionality provided by this module
Description = 'A Cross-Platform PowerShell framework for creating web servers to host REST APIs and Websites. Pode also has support for being used in Azure Functions and AWS Lambda.'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '5.0'
PowerShellVersion = '5.1'

# Functions to export from this Module
FunctionsToExport = @(
Expand Down Expand Up @@ -293,6 +293,7 @@
'Get-PodeEndpoint',
'Pode',
'Get-PodeServerDefaultSecret',
'Get-PodeVersion',

# openapi
'Enable-PodeOpenApi',
Expand Down Expand Up @@ -456,14 +457,16 @@

# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PSData = @{
PSData = @{

# Tags applied to this module. These help with module discovery in online galleries.
Tags = @('powershell', 'web', 'server', 'http', 'listener', 'rest', 'api', 'tcp', 'smtp', 'websites',
'powershell-core', 'windows', 'unix', 'linux', 'pode', 'PSEdition_Core', 'cross-platform',
'file-monitoring', 'multithreaded', 'schedule', 'middleware', 'session',
'authentication', 'authorisation', 'arm', 'raspberry-pi', 'aws-lambda',
'azure-functions', 'websockets', 'swagger', 'openapi', 'webserver', 'secrets', 'fim')
Tags = @(
'powershell', 'web', 'server', 'http', 'https', 'listener', 'rest', 'api', 'tcp',
'smtp', 'websites', 'powershell-core', 'windows', 'unix', 'linux', 'pode', 'PSEdition_Core',
'cross-platform', 'file-monitoring', 'multithreaded', 'schedule', 'middleware', 'session',
'authentication', 'authorisation', 'authorization', 'arm', 'raspberry-pi', 'aws-lambda',
'azure-functions', 'websockets', 'swagger', 'openapi', 'webserver', 'secrets', 'fim'
)

# A URL to the license for this module.
LicenseUri = 'https://raw.githubusercontent.com/Badgerati/Pode/master/LICENSE.txt'
Expand All @@ -478,5 +481,7 @@
ReleaseNotes = 'https://github.com/Badgerati/Pode/releases/tag/v$version$'

}
PwshVersionsUntested = '$versionUntested$'
PwshVersionSupported = '$versionSupported$'
mdaneri marked this conversation as resolved.
Show resolved Hide resolved
}
}
50 changes: 39 additions & 11 deletions src/Pode.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,35 @@ $root = Split-Path -Parent -Path $MyInvocation.MyCommand.Path
Add-Type -AssemblyName System.Web
Add-Type -AssemblyName System.Net.Http

# netstandard2 for <7.2
if ($PSVersionTable.PSVersion -lt [version]'7.2.0') {
Add-Type -LiteralPath "$($root)/Libs/netstandard2.0/Pode.dll" -ErrorAction Stop
}
# net6 for =7.2
elseif ($PSVersionTable.PSVersion -lt [version]'7.3.0') {
Add-Type -LiteralPath "$($root)/Libs/net6.0/Pode.dll" -ErrorAction Stop
# Construct the path to the module manifest (.psd1 file)
$moduleManifestPath = Join-Path -Path $root -ChildPath 'Pode.psd1'

# Import the module manifest to access its properties
$moduleManifest = Import-PowerShellDataFile -Path $moduleManifestPath

$podeDll = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq 'Pode' }

if ($podeDll) {
if ( $moduleManifest.ModuleVersion -ne '$version$') {
$moduleVersion = ([version]::new($moduleManifest.ModuleVersion + '.0'))
if ($podeDll.GetName().Version -ne $moduleVersion) {
throw "An existing incompatible Pode.DLL version $($podeDll.GetName().Version) is loaded. Version $moduleVersion is required. Open a new Powershell/pwsh session and retry."
}
}
}
# net7 for >7.2
else {
Add-Type -LiteralPath "$($root)/Libs/net7.0/Pode.dll" -ErrorAction Stop
if ($PSVersionTable.PSVersion -ge [version]'7.4.0') {
Add-Type -LiteralPath "$($root)/Libs/net8.0/Pode.dll" -ErrorAction Stop
}
elseif ($PSVersionTable.PSVersion -ge [version]'7.3.0') {
Add-Type -LiteralPath "$($root)/Libs/net7.0/Pode.dll" -ErrorAction Stop
}
elseif ($PSVersionTable.PSVersion -ge [version]'7.2.0') {
Add-Type -LiteralPath "$($root)/Libs/net6.0/Pode.dll" -ErrorAction Stop
}
else {
Add-Type -LiteralPath "$($root)/Libs/netstandard2.0/Pode.dll" -ErrorAction Stop
}
}

# load private functions
Expand All @@ -24,11 +42,21 @@ Get-ChildItem "$($root)/Private/*.ps1" | ForEach-Object { . ([System.IO.Path]::G
# only import public functions
$sysfuncs = Get-ChildItem Function:

# only import public alias
$sysaliases = Get-ChildItem Alias:

# load public functions
Get-ChildItem "$($root)/Public/*.ps1" | ForEach-Object { . ([System.IO.Path]::GetFullPath($_)) }

# get functions from memory and compare to existing to find new functions added
$funcs = Get-ChildItem Function: | Where-Object { $sysfuncs -notcontains $_ }

$aliases = Get-ChildItem Alias: | Where-Object { $sysaliases -notcontains $_ }
# export the module's public functions
Export-ModuleMember -Function ($funcs.Name)
if ($funcs) {
if ($aliases) {
Export-ModuleMember -Function ($funcs.Name) -Alias $aliases.Name
}
else {
Export-ModuleMember -Function ($funcs.Name)
}
}
124 changes: 123 additions & 1 deletion src/Private/Helpers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2899,4 +2899,126 @@ function Test-PodePlaceholders {
}

return ($Path -imatch $Placeholder)
}
}


<#
.SYNOPSIS
Retrieves the PowerShell module manifest object for the specified module.

.DESCRIPTION
This function constructs the path to a PowerShell module manifest file (.psd1) located in the parent directory of the script root. It then imports the module manifest file to access its properties and returns the manifest object. This can be useful for scripts that need to dynamically discover and utilize module metadata, such as version, dependencies, and exported functions.

.PARAMETERS
This function does not accept any parameters.

.EXAMPLE
$manifest = Get-PodeModuleManifest
This example calls the `Get-PodeModuleManifest` function to retrieve the module manifest object and stores it in the variable `$manifest`.

#>
function Get-PodeModuleManifest {
# Construct the path to the module manifest (.psd1 file)
$moduleManifestPath = Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'Pode.psd1'

# Import the module manifest to access its properties
$moduleManifest = Import-PowerShellDataFile -Path $moduleManifestPath
return $moduleManifest
}


<#
.SYNOPSIS
Tests if the Pode module is from the development branch.

.DESCRIPTION
The Test-PodeVersionDev function checks if the Pode module's version matches the placeholder value ('$version$'), which is used to indicate the development branch of the module. It returns $true if the version matches, indicating the module is from the development branch, and $false otherwise.

.PARAMETER None
This function does not accept any parameters.

.OUTPUTS
System.Boolean
Returns $true if the Pode module version is '$version$', indicating the development branch. Returns $false for any other version.

.EXAMPLE
PS> $moduleManifest = @{ ModuleVersion = '$version$' }
PS> Test-PodeVersionDev

Returns $true, indicating the development branch.

.EXAMPLE
PS> $moduleManifest = @{ ModuleVersion = '1.2.3' }
PS> Test-PodeVersionDev

Returns $false, indicating a specific release version.

.NOTES
This function assumes that $moduleManifest is a hashtable representing the loaded module manifest, with a key of ModuleVersion.

#>
function Test-PodeVersionDev {
return (Get-PodeModuleManifest).ModuleVersion -eq '$version$'
}

<#
.SYNOPSIS
Tests the running PowerShell version for compatibility with Pode, identifying end-of-life (EOL) and untested versions.

.DESCRIPTION
The `Test-PodeVersionPwshEOL` function checks the current PowerShell version against a list of versions that were either supported or EOL at the time of the Pode release. It uses the module manifest to determine which PowerShell versions are considered EOL and which are officially supported. If the current version is EOL or was not tested with the current release of Pode, the function generates a warning. This function aids in maintaining best practices for using supported PowerShell versions with Pode.

.PARAMETER ReportUntested
If specified, the function will report if the current PowerShell version was not available and thus untested at the time of the Pode release. This is useful for identifying potential compatibility issues with newer versions of PowerShell.

.OUTPUTS
A hashtable containing two keys:
- `eol`: A boolean indicating if the current PowerShell version was EOL at the time of the Pode release.
- `supported`: A boolean indicating if the current PowerShell version was officially supported by Pode at the time of the release.

.EXAMPLE
Test-PodeVersionPwshEOL

Checks the current PowerShell version against Pode's supported and EOL versions list. Outputs a warning if the version is EOL or untested, and returns a hashtable indicating the compatibility status.

.EXAMPLE
Test-PodeVersionPwshEOL -ReportUntested

Similar to the basic usage, but also reports if the current PowerShell version was untested because it was not available at the time of the Pode release.

.NOTES
This function is part of the Pode module's utilities to ensure compatibility and encourage the use of supported PowerShell versions.

#>
function Test-PodeVersionPwshEOL {
param(
[switch] $ReportUntested
)
$moduleManifest = Get-PodeModuleManifest
if ($moduleManifest.ModuleVersion -eq '$version$') {
return @{
eol = $false
supported = $true
}
}

$psVersion = $PSVersionTable.PSVersion
$eolVersions = $moduleManifest.PrivateData.PwshVersionsUntested -split ','
$isEol = "$($psVersion.Major).$($psVersion.Minor)" -in $eolVersions

if ($isEol) {
mdaneri marked this conversation as resolved.
Show resolved Hide resolved
Write-PodeHost "[WARNING] The running version of PowerShell $($PSVersionTable.PSVersion), was EOL when Pode $(Get-PodeVersion) was released. Pode should work but has not been tested on this version of PowerShell." -ForegroundColor Yellow
}

$SupportedVersions = $moduleManifest.PrivateData.PwshVersionSupported -split ','
$isSupported = "$($psVersion.Major).$($psVersion.Minor)" -in $SupportedVersions

if (! $isSupported -and $ReportUntested) {
Write-PodeHost "[WARNING] The running version of PowerShell $($PSVersionTable.PSVersion), was not available when Pode $(Get-PodeVersion) was released. Pode should work but has not been tested on this version of PowerShell." -ForegroundColor Yellow
mdaneri marked this conversation as resolved.
Show resolved Hide resolved
}

return @{
eol = $isEol
supported = $isSupported
}
}
4 changes: 4 additions & 0 deletions src/Private/Server.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,12 @@ function Start-PodeInternalServer {
# run running event hooks
Invoke-PodeEvent -Type Running

# Check if the running version of Powershell is EOL
$null = Test-PodeVersionPwshEOL
mdaneri marked this conversation as resolved.
Show resolved Hide resolved

# state what endpoints are being listened on
if ($endpoints.Length -gt 0) {
Write-PodeHost "Pode $(Get-PodeVersion) (PID: $($PID))" -ForegroundColor Yellow
Write-PodeHost "Listening on the following $($endpoints.Length) endpoint(s) [$($PodeContext.Threads.General) thread(s)]:" -ForegroundColor Yellow
$endpoints | ForEach-Object {
$flags = @()
Expand Down
Empty file added src/Private/Version.ps1
mdaneri marked this conversation as resolved.
Show resolved Hide resolved
Empty file.
Loading
Loading