Skip to content

Commit 169c576

Browse files
🪲 [Fix]: Wildcard maximum versions no longer fail module processing (#446)
Module requirements can now use wildcard maximum versions such as `1.*` without module builds or installations failing. Versions remain constrained to the requested major or minor release line, while concrete maximum versions keep their existing behavior. ## Fixed: wildcard maximum versions no longer fail module builds Module manifests can now preserve wildcard maximum-version requirements instead of rejecting them as invalid version values. A requirement such as `MaximumVersion = '1.*'` remains usable during manifest generation. ## Fixed: wildcard maximum versions resolve to the correct release range Module installation translates wildcard maximum versions into an exclusive upper bound. For example, `MinimumVersion = '1.0.0'` and `MaximumVersion = '1.*'` resolve to the range `[1.0.0,2.0.0)`, allowing 1.x releases without admitting 2.x releases. Unsupported wildcard patterns are reported clearly. --- <details> <summary>Technical details</summary> - Manifest processing now keeps wildcard maximum versions as strings and only creates a numeric comparison bound for concrete versions. - Installation version-spec conversion maps `1.*` to the exclusive upper bound `2.0.0` and `1.2.*` to `1.3.0`; concrete maximum versions remain inclusive. - Source test fixtures now exercise a `ThreadJob` requirement with `ModuleVersion = '1.0.0'` and `MaximumVersion = '1.*'`. - The branch was synchronized with `main`; the repository's native Zensical workflow remains unchanged and continues to build directly from `zensical.toml`. - Implementation plan progress: the scoped delivery bug in #444 is completed by this pull request. | Changed surface | Standards checked | Framework docs checked | Result | | --- | --- | --- | --- | | `.github/actions/**` (PowerShell) | Naming, functions, action layout | Process-PSModule action conventions | Aligned | | `tests/**` (PowerShell) | Pester fixture conventions | Module test repository layout | Aligned | </details> <details> <summary>Relevant issues (or links)</summary> - Fixes #444 </details> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 1653be8 commit 169c576

4 files changed

Lines changed: 35 additions & 12 deletions

File tree

‎.github/actions/Build-PSModule/src/helpers/Build/Build-PSModuleManifest.ps1‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@
260260
Write-Host "Processing required module [$requiredModuleName]"
261261
$requiredVersion = $group.Group.RequiredVersion | ForEach-Object { [Version]$_ } | Sort-Object -Unique
262262
$minimumVersion = $group.Group.Version | ForEach-Object { [Version]$_ } | Sort-Object -Unique | Select-Object -Last 1
263-
$maximumVersion = $group.Group.MaximumVersion | ForEach-Object { [Version]$_ } | Sort-Object -Unique | Select-Object -First 1
263+
$maximumVersion = $group.Group.MaximumVersion | Sort-Object -Unique | Select-Object -First 1
264+
$maximumVersionBound = if ($maximumVersion -and ($maximumVersion -notmatch '[*]')) { [Version]$maximumVersion } else { $null }
264265
Write-Host "RequiredVersion: [$($requiredVersion -join ', ')]"
265266
Write-Host "ModuleVersion: [$minimumVersion]"
266267
Write-Host "MaximumVersion: [$maximumVersion]"
@@ -274,17 +275,17 @@
274275
}
275276

276277
if (-not $maximumVersion) {
277-
$maximumVersion = [Version]'9999.9999.9999'
278+
$maximumVersionBound = [Version]'9999.9999.9999'
278279
}
279280

280281
if ($requiredVersion -and ($minimumVersion -gt $requiredVersion)) {
281282
throw 'ModuleVersion is higher than RequiredVersion.'
282283
}
283284

284-
if ($minimumVersion -gt $maximumVersion) {
285+
if ($maximumVersionBound -and ($minimumVersion -gt $maximumVersionBound)) {
285286
throw 'ModuleVersion is higher than MaximumVersion.'
286287
}
287-
if ($requiredVersion -and ($requiredVersion -gt $maximumVersion)) {
288+
if ($requiredVersion -and $maximumVersionBound -and ($requiredVersion -gt $maximumVersionBound)) {
288289
throw 'RequiredVersion is higher than MaximumVersion.'
289290
}
290291

@@ -294,15 +295,15 @@
294295
ModuleName = $requiredModuleName
295296
RequiredVersion = $requiredVersion
296297
}
297-
} elseif (($minimumVersion -ne [Version]'0.0.0') -or ($maximumVersion -ne [Version]'9999.9999.9999')) {
298+
} elseif (($minimumVersion -ne [Version]'0.0.0') -or ($maximumVersionBound -ne [Version]'9999.9999.9999')) {
298299
Write-Host '[RequiredModules] - ModuleVersion/MaximumVersion'
299300
$uniqueModule = @{
300301
ModuleName = $requiredModuleName
301302
}
302303
if ($minimumVersion -ne [Version]'0.0.0') {
303304
$uniqueModule['ModuleVersion'] = $minimumVersion
304305
}
305-
if ($maximumVersion -ne [Version]'9999.9999.9999') {
306+
if ($maximumVersionBound -ne [Version]'9999.9999.9999') {
306307
$uniqueModule['MaximumVersion'] = $maximumVersion
307308
}
308309
} else {

‎.github/actions/Install-PSModule/src/PSModule/PSModule.psm1‎

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,40 @@ function Convert-VersionSpec {
8787
[string] $RequiredVersion
8888
)
8989

90+
$maximumVersionIsWildcard = $MaximumVersion -and ($MaximumVersion -match '[*]')
91+
if ($maximumVersionIsWildcard) {
92+
$versionParts = @($MaximumVersion -split '\.')
93+
$wildcardIndex = [Array]::IndexOf($versionParts, '*')
94+
if ($wildcardIndex -lt 0) {
95+
throw "MaximumVersion '$MaximumVersion' contains an unsupported wildcard pattern."
96+
}
97+
98+
if ($wildcardIndex -eq 0) {
99+
throw "MaximumVersion '$MaximumVersion' contains an unsupported wildcard pattern."
100+
}
101+
102+
$upperBoundParts = @(0, 0, 0)
103+
for ($i = 0; $i -lt $wildcardIndex; $i++) {
104+
$upperBoundParts[$i] = [int]$versionParts[$i]
105+
}
106+
$upperBoundParts[$wildcardIndex - 1]++
107+
$MaximumVersion = $upperBoundParts -join '.'
108+
}
109+
90110
if ($RequiredVersion) {
91111
# Use exact match in bracket notation.
92112
return "[$RequiredVersion]"
93113
} elseif ($MinimumVersion -and $MaximumVersion) {
94-
# Both bounds provided; both are inclusive.
95-
return "[$MinimumVersion,$MaximumVersion]"
114+
# Wildcard maximum versions are translated to an exclusive upper bound.
115+
$upperBound = $maximumVersionIsWildcard ? ')' : ']'
116+
return "[$MinimumVersion,$MaximumVersion$upperBound"
96117
} elseif ($MinimumVersion) {
97118
# Only a minimum is provided. Use a minimum-inclusive range.
98119
return "[$MinimumVersion, ]"
99120
} elseif ($MaximumVersion) {
100-
# Only a maximum is provided; lower bound open.
101-
return "(, $MaximumVersion]"
121+
# Only a maximum is provided; lower bound open. Wildcards are exclusive upper bounds.
122+
$upperBound = $maximumVersionIsWildcard ? ')' : ']'
123+
return "(, $MaximumVersion$upperBound"
102124
} else {
103125
return $null
104126
}

‎tests/srcTestRepo/src/functions/public/PSModule/Get-PSModuleTest.ps1‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Requires -Modules Utilities
22
#Requires -Modules @{ ModuleName = 'PSSemVer'; RequiredVersion = '1.1.4' }
3-
#Requires -Modules @{ ModuleName = 'DynamicParams'; ModuleVersion = '1.1.8' }
3+
#Requires -Modules @{ ModuleName = 'ThreadJob'; ModuleVersion = '1.0.0'; MaximumVersion = '1.*' }
44
#Requires -Modules @{ ModuleName = 'Store'; ModuleVersion = '0.3.1' }
55

66
function Get-PSModuleTest {

‎tests/srcWithManifestTestRepo/src/functions/public/PSModule/Get-PSModuleTest.ps1‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Requires -Modules Utilities
22
#Requires -Modules @{ ModuleName = 'PSSemVer'; RequiredVersion = '1.1.4' }
3-
#Requires -Modules @{ ModuleName = 'DynamicParams'; ModuleVersion = '1.1.8' }
3+
#Requires -Modules @{ ModuleName = 'ThreadJob'; ModuleVersion = '1.0.0'; MaximumVersion = '1.*' }
44
#Requires -Modules @{ ModuleName = 'Store'; ModuleVersion = '0.3.1' }
55

66
function Get-PSModuleTest {

0 commit comments

Comments
 (0)