diff --git a/pode.build.ps1 b/pode.build.ps1 index 3222d086e..effb1b938 100644 --- a/pode.build.ps1 +++ b/pode.build.ps1 @@ -9,6 +9,14 @@ .PARAMETER Version Specifies the project version for stamping, packaging, and documentation. Defaults to '0.0.0'. +.PARAMETER Prerelease + Specifies the prerelease label to append to the module version, following semantic versioning conventions. + Examples include 'alpha.1', 'alpha.2', 'beta.1', etc. This label indicates the stability and iteration of the prerelease version. + +.PARAMETER PersistVersion + If specified, the provided version and prerelease label will be saved to `Version.json`. + This ensures future builds use the same versioning information unless explicitly overridden. + .PARAMETER PesterVerbosity Sets the verbosity level for Pester tests. Options: None, Normal, Detailed, Diagnostic. @@ -76,6 +84,9 @@ Invoke-Build -Task Docs # Builds and serves the documentation locally. + Invoke-Build -Task Build -Version '2.13.0' -Prerelease 'beta.1' -PersistVersion + # Saves "2.13.0-beta.1" to Version.json for future builds. + .LINK For more information, visit https://github.com/Badgerati/Pode #> @@ -89,7 +100,13 @@ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPositionalParameters', '')] param( [string] - $Version = '0.0.0', + $Version, + + [string] + $Prerelease, + + [switch] + $PersistVersion, [string] [ValidateSet('None', 'Normal' , 'Detailed', 'Diagnostic')] @@ -488,18 +505,27 @@ function Invoke-PodeBuildDotnetBuild { # Optionally set assembly version if ($Version) { - Write-Output "Assembly Version: $Version" - $AssemblyVersion = "-p:Version=$Version" + if ($Prerelease) { + Write-Output "Assembly Version: $Version-$Prerelease" + $AssemblyVersion = "-p:VersionPrefix=$Version" + $AssemblyPrerelease = "-p:VersionSuffix=$Prerelease" + } + else { + Write-Output "Assembly Version: $Version" + $AssemblyVersion = "-p:Version=$Version" + $AssemblyPrerelease = '' + } } else { $AssemblyVersion = '' + $AssemblyPrerelease = '' } # restore dependencies dotnet restore # Use dotnet publish for .NET Core and .NET 5+ - dotnet publish --configuration Release --self-contained --framework $target $AssemblyVersion --output ../Libs/$target + dotnet publish --configuration Release --self-contained --framework $target $AssemblyVersion $AssemblyPrerelease --output ../Libs/$target if (!$?) { throw "Build failed for target framework '$target'." @@ -889,6 +915,56 @@ if (($null -eq $PSCmdlet.MyInvocation) -or ($PSCmdlet.MyInvocation.BoundParamete return } +# Import Version File if needed +if ([string]::IsNullOrEmpty($Version)) { + if (Test-Path './Version.json' -PathType Leaf) { + $importedVersion = Get-Content -Path './Version.json' | ConvertFrom-Json + if ($importedVersion.Version) { + $Version = $importedVersion.Version + } + if ($importedVersion.Prerelease) { + $Prerelease = $importedVersion.Prerelease + } + } + else { + $Version = '0.0.0' + if ($PersistVersion) { + Write-Error 'The -PersistVersion parameter requires the -Version parameter to be specified.' + return + } + } +} +elseif ($PersistVersion) { + if ($Prerelease) { + [ordered]@{Version = $Version; Prerelease = $Prerelease } | ConvertTo-Json | Out-File './Version.json' + } + else { + [ordered]@{Version = $Version } | ConvertTo-Json | Out-File './Version.json' + } +} + + +Write-Host '---------------------------------------------------' -ForegroundColor DarkCyan + +# Display the Pode build version +if ($Prerelease) { + Write-Host "Pode Build: v$Version-$Prerelease (Pre-release)" -ForegroundColor DarkCyan +} +else { + if ($Version -eq '0.0.0') { + Write-Host 'Pode Build: [Development Version]' -ForegroundColor DarkCyan + } + else { + Write-Host "Pode Build: v$Version" -ForegroundColor DarkCyan + } +} + +# Display the current UTC time in a readable format +$utcTime = (Get-Date).ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss 'UTC'") +Write-Host "Start Time: $utcTime" -ForegroundColor DarkCyan + +Write-Host '---------------------------------------------------' -ForegroundColor DarkCyan + Add-BuildTask Default { Write-Host 'Tasks in the Build Script:' -ForegroundColor DarkMagenta @@ -939,7 +1015,13 @@ Add-BuildTask Default { # Synopsis: Stamps the version onto the Module Add-BuildTask StampVersion { $pwshVersions = Get-PodeBuildPwshEOL - (Get-Content ./pkg/Pode.psd1) | ForEach-Object { $_ -replace '\$version\$', $Version -replace '\$versionsUntested\$', $pwshVersions.eol -replace '\$versionsSupported\$', $pwshVersions.supported -replace '\$buildyear\$', ((get-date).Year) } | Set-Content ./pkg/Pode.psd1 + $prereleaseValue = if ($Prerelease) { + "Prerelease = '$Prerelease'" + } + else { + '' + } + (Get-Content ./pkg/Pode.psd1) | ForEach-Object { $_ -replace '\$version\$', $Version -replace '\$versionsUntested\$', $pwshVersions.eol -replace '\$versionsSupported\$', $pwshVersions.supported -replace '\$buildyear\$', ((get-date).Year) -replace '#\$Prerelease-Here\$', $prereleaseValue } | 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 @@ -1682,7 +1764,7 @@ Add-BuildTask SetupPowerShell { #> # Synopsis: Build the Release Notes -task ReleaseNotes { +Add-BuildTask ReleaseNotes { if ([string]::IsNullOrWhiteSpace($ReleaseNoteVersion)) { Write-Host 'Please provide a ReleaseNoteVersion' -ForegroundColor Red return @@ -1818,3 +1900,4 @@ task ReleaseNotes { Write-Host '' } } + diff --git a/src/Pode.psd1 b/src/Pode.psd1 index aff2bfcb7..437292271 100644 --- a/src/Pode.psd1 +++ b/src/Pode.psd1 @@ -547,6 +547,9 @@ # 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 = @{ + + #$Prerelease-Here$ + # Tags applied to this module. These help with module discovery in online galleries. Tags = @( 'powershell', 'web', 'server', 'http', 'https', 'listener', 'rest', 'api', 'tcp', diff --git a/src/Pode.psm1 b/src/Pode.psm1 index 34a812c3a..ed20fe6aa 100644 --- a/src/Pode.psm1 +++ b/src/Pode.psm1 @@ -49,6 +49,32 @@ if ([string]::IsNullOrEmpty($UICulture)) { $UICulture = $PsUICulture } + +function Test-PodeAssembly { + $podeDll = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq 'Pode' } + + if ($podeDll) { + if ( $PodeManifest.ModuleVersion -ne '$version$') { + $moduleVersion = ([version]::new($PodeManifest.ModuleVersion + '.0')) + if ($podeDll.GetName().Version -ne $moduleVersion) { + # An existing incompatible Pode.DLL version {0} is loaded. Version {1} is required. Open a new Powershell/pwsh session and retry. + throw ($PodeLocale.incompatiblePodeDllExceptionMessage -f $podeDll.GetName().Version, $moduleVersion) + } + $assemblyInformationalVersion = $podeDll.CustomAttributes.Where({ $_.AttributeType -eq [System.Reflection.AssemblyInformationalVersionAttribute] }) + if ($null -ne $PodeManifest.PrivateData.PSData.Prerelease) { + if (! $assemblyInformationalVersion.ConstructorArguments.Value.Contains($PodeManifest.PrivateData.PSData.Prerelease)) { + throw ($PodeLocale.incompatiblePodeDllExceptionMessage -f $assemblyInformationalVersion.ConstructorArguments.Value, "$moduleVersion-$($PodeManifest.PrivateData.PSData.Prerelease)") + } + } + elseif ($assemblyInformationalVersion.ConstructorArguments.Value.Contains('-')) { + throw ($PodeLocale.incompatiblePodeDllExceptionMessage -f $assemblyInformationalVersion.ConstructorArguments.Value, $moduleVersion) + } + } + return $true + } + return $false +} + try { try { #The list of all available supported culture is available here https://azuliadesigns.com/c-sharp-tutorials/list-net-culture-country-codes/ @@ -75,21 +101,9 @@ try { $moduleManifestPath = Join-Path -Path $root -ChildPath 'Pode.psd1' # Import the module manifest to access its properties - $moduleManifest = Import-PowerShellDataFile -Path $moduleManifestPath -ErrorAction Stop - - - $podeDll = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq 'Pode' } + $PodeManifest = Import-PowerShellDataFile -Path $moduleManifestPath -ErrorAction Stop - if ($podeDll) { - if ( $moduleManifest.ModuleVersion -ne '$version$') { - $moduleVersion = ([version]::new($moduleManifest.ModuleVersion + '.0')) - if ($podeDll.GetName().Version -ne $moduleVersion) { - # An existing incompatible Pode.DLL version {0} is loaded. Version {1} is required. Open a new Powershell/pwsh session and retry. - throw ($PodeLocale.incompatiblePodeDllExceptionMessage -f $podeDll.GetName().Version, $moduleVersion) - } - } - } - else { + if (! (Test-PodeAssembly)) { # fetch the .net version and the libs path $version = [System.Environment]::Version.Major $libsPath = "$($root)/Libs" @@ -109,6 +123,7 @@ try { # append Pode.dll and mount Add-Type -LiteralPath "$($netFolder)/Pode.dll" -ErrorAction Stop + $null = Test-PodeAssembly } # load private functions @@ -143,4 +158,3 @@ finally { # Cleanup temporary variables Remove-Variable -Name 'tmpPodeLocale', 'localesPath', 'moduleManifest', 'root', 'version', 'libsPath', 'netFolder', 'podeDll', 'sysfuncs', 'sysaliases', 'funcs', 'aliases', 'moduleManifestPath', 'moduleVersion' -ErrorAction SilentlyContinue } - diff --git a/src/Public/Utilities.ps1 b/src/Public/Utilities.ps1 index e193bbbc3..1d874a4d7 100644 --- a/src/Public/Utilities.ps1 +++ b/src/Public/Utilities.ps1 @@ -1361,8 +1361,8 @@ Gets the version of the Pode module. .DESCRIPTION The Get-PodeVersion function checks the version of the Pode module specified in the module manifest. If the module version is not a placeholder value ('$version$'), it returns the actual version prefixed with 'v.'. If the module version is the placeholder value, indicating the development branch, it returns '[develop branch]'. -.PARAMETER None -This function does not accept any parameters. +.PARAMETER Raw +If this switch is set, the function will return the raw version number as a [version] object instead of a string. .OUTPUTS System.String @@ -1385,9 +1385,22 @@ This function assumes that $moduleManifest is a hashtable representing the loade #> function Get-PodeVersion { + param( + [switch] + $Raw + ) $moduleManifest = Get-PodeModuleManifest if ($moduleManifest.ModuleVersion -ne '$version$') { - return "v$($moduleManifest.ModuleVersion)" + $version = if ( $Raw) { + $moduleManifest.ModuleVersion + } + else { + "v$($moduleManifest.ModuleVersion)" + } + if ($moduleManifest.PrivateData.PSData.Prerelease) { + return "$version-$($moduleManifest.PrivateData.PSData.Prerelease)" + } + return $version } else { return '[dev]'