Skip to content

Commit 7ce9a25

Browse files
Merge branch 'main' into dependabot/github_actions/github-actions-7a5a078ad4
2 parents 202089d + 1653be8 commit 7ce9a25

9 files changed

Lines changed: 895 additions & 28 deletions

File tree

.github/actions/Publish-PSModule/action.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ inputs:
3737
description: Name of the uploaded artifact to download. Must match the name used in the upstream upload-artifact step.
3838
required: false
3939
default: module
40+
VersionPrefix:
41+
description: |
42+
Prefix put in front of the version in the git tag of the GitHub release, for example 'v'.
43+
Comes from Settings.Publish.Module.VersionPrefix, which the Plan job resolves. The compiled manifest carries the module version as
44+
Major.Minor.Patch and cannot carry the prefix, so it is supplied here. An empty value tags the release without a prefix.
45+
required: false
46+
default: ''
4047

4148
runs:
4249
using: composite
@@ -65,4 +72,5 @@ runs:
6572
PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRBodyAsReleaseNotes: ${{ inputs.UsePRBodyAsReleaseNotes }}
6673
PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsReleaseName: ${{ inputs.UsePRTitleAsReleaseName }}
6774
PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsNotesHeading: ${{ inputs.UsePRTitleAsNotesHeading }}
75+
PSMODULE_PUBLISH_PSMODULE_INPUT_VersionPrefix: ${{ inputs.VersionPrefix }}
6876
run: ${{ github.action_path }}/src/publish.ps1
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
function Get-ModuleVersionString {
2+
<#
3+
.SYNOPSIS
4+
Builds the SemVer version string that identifies the module itself.
5+
6+
.DESCRIPTION
7+
Composes the module version and, when there is one, the prerelease label. This is the string the
8+
PowerShell Gallery and the module manifest understand: `Major.Minor.Patch` optionally followed by
9+
`-<prerelease>`. It never carries the repository's version prefix, because neither the manifest's
10+
`ModuleVersion` nor a Gallery package version accepts one.
11+
12+
.OUTPUTS
13+
String with the module version.
14+
15+
.EXAMPLE
16+
Get-ModuleVersionString -ModuleVersion '1.1.10'
17+
18+
Returns '1.1.10'.
19+
20+
.EXAMPLE
21+
Get-ModuleVersionString -ModuleVersion '1.1.10' -Prerelease 'mybranch001'
22+
23+
Returns '1.1.10-mybranch001'.
24+
#>
25+
[CmdletBinding()]
26+
[OutputType([string])]
27+
param(
28+
# The module version from the compiled manifest, in Major.Minor.Patch format.
29+
[Parameter(Mandatory)]
30+
[ValidateNotNullOrEmpty()]
31+
[string] $ModuleVersion,
32+
33+
# The prerelease label from the compiled manifest. Empty for a stable release.
34+
[Parameter()]
35+
[AllowEmptyString()]
36+
[AllowNull()]
37+
[string] $Prerelease
38+
)
39+
40+
if ([string]::IsNullOrWhiteSpace($Prerelease)) {
41+
return $ModuleVersion
42+
}
43+
44+
"$ModuleVersion-$($Prerelease.Trim())"
45+
}
46+
47+
function Get-ReleaseTag {
48+
<#
49+
.SYNOPSIS
50+
Builds the git tag used for the GitHub release.
51+
52+
.DESCRIPTION
53+
Prefixes the module's SemVer version string with the configured version prefix. The version comes
54+
from the compiled manifest, which is the artifact that is published, so the tag always names the
55+
exact bytes that were tested and pushed to the PowerShell Gallery. The manifest's ModuleVersion is
56+
Major.Minor.Patch by definition and cannot carry the prefix, so the prefix is supplied from the
57+
resolved settings (Publish.Module.VersionPrefix) instead.
58+
59+
The prefix belongs to the GitHub release tag and to nothing else. PowerShell manifests and Gallery
60+
package versions only accept plain SemVer, so callers that need the module's own version use
61+
Get-ModuleVersionString. Deriving both from the same composition keeps the prefix as the only
62+
difference between them.
63+
64+
.OUTPUTS
65+
String with the release tag.
66+
67+
.EXAMPLE
68+
Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10'
69+
70+
Returns 'v1.1.10'.
71+
72+
.EXAMPLE
73+
Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' -Prerelease 'mybranch001'
74+
75+
Returns 'v1.1.10-mybranch001'.
76+
77+
.EXAMPLE
78+
Get-ReleaseTag -VersionPrefix '' -ModuleVersion '1.1.10'
79+
80+
Returns '1.1.10'.
81+
#>
82+
[CmdletBinding()]
83+
[OutputType([string])]
84+
param(
85+
# The module version from the compiled manifest, in Major.Minor.Patch format.
86+
[Parameter(Mandatory)]
87+
[ValidateNotNullOrEmpty()]
88+
[string] $ModuleVersion,
89+
90+
# The prefix put in front of the version, for example 'v'. Empty for an unprefixed repository.
91+
[Parameter()]
92+
[AllowEmptyString()]
93+
[AllowNull()]
94+
[string] $VersionPrefix,
95+
96+
# The prerelease label from the compiled manifest. Empty for a stable release.
97+
[Parameter()]
98+
[AllowEmptyString()]
99+
[AllowNull()]
100+
[string] $Prerelease
101+
)
102+
103+
"$($VersionPrefix.Trim())$(Get-ModuleVersionString -ModuleVersion $ModuleVersion -Prerelease $Prerelease)"
104+
}

.github/actions/Publish-PSModule/src/publish.ps1

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ param()
2828
$PSStyle.OutputRendering = 'Ansi'
2929

3030
Import-Module -Name 'PSModule' -Force
31+
Import-Module -Name "$PSScriptRoot/Publish-PSModule.Helpers.psm1" -Force
3132

3233
#region Load inputs
3334
LogGroup 'Load inputs' {
@@ -58,10 +59,14 @@ LogGroup 'Load inputs' {
5859
$usePRBodyAsReleaseNotes = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRBodyAsReleaseNotes -eq 'true'
5960
$usePRTitleAsReleaseName = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsReleaseName -eq 'true'
6061
$usePRTitleAsNotesHeading = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_UsePRTitleAsNotesHeading -eq 'true'
62+
# The prefix the repository tags releases with, resolved by the Plan job from
63+
# Settings.Publish.Module.VersionPrefix. Empty means the repository tags without a prefix.
64+
$versionPrefix = $env:PSMODULE_PUBLISH_PSMODULE_INPUT_VersionPrefix
6165

62-
Write-Host "Module name: [$name]"
63-
Write-Host "Module path: [$modulePath]"
64-
Write-Host "WhatIf: [$whatIf]"
66+
Write-Host "Module name: [$name]"
67+
Write-Host "Module path: [$modulePath]"
68+
Write-Host "Version prefix: [$versionPrefix]"
69+
Write-Host "WhatIf: [$whatIf]"
6570
}
6671
#endregion Load inputs
6772

@@ -129,12 +134,18 @@ LogGroup 'Resolve version from manifest' {
129134
$createPrerelease = $true
130135
}
131136

132-
$releaseTag = if ($createPrerelease) { "$moduleVersion-$prerelease" } else { $moduleVersion }
137+
# The PowerShell Gallery and the module manifest only accept plain SemVer, so the configured
138+
# VersionPrefix is applied to the GitHub release tag and to nothing else. Both strings are derived
139+
# from the same composition here, so the prefix is the only difference between them.
140+
$publishPSVersion = Get-ModuleVersionString -ModuleVersion $moduleVersion -Prerelease $prerelease
141+
$releaseTag = Get-ReleaseTag -VersionPrefix $versionPrefix -ModuleVersion $moduleVersion -Prerelease $prerelease
133142

134143
[PSCustomObject]@{
135144
ModuleVersion = $moduleVersion
145+
VersionPrefix = $versionPrefix
136146
Prerelease = $prerelease
137147
CreatePrerelease = $createPrerelease
148+
GalleryVersion = $publishPSVersion
138149
ReleaseTag = $releaseTag
139150
PRNumber = $prNumber
140151
PRHeadRef = $prHeadRef
@@ -154,7 +165,6 @@ LogGroup 'Install module dependencies' {
154165
#region Publish to PSGallery
155166
LogGroup 'Publish to PSGallery' {
156167
$releaseType = if ($createPrerelease) { 'New prerelease' } else { 'New release' }
157-
$publishPSVersion = if ($createPrerelease) { "$moduleVersion-$prerelease" } else { $moduleVersion }
158168
$psGalleryReleaseLink = "https://www.powershellgallery.com/packages/$name/$publishPSVersion"
159169

160170
Write-Host 'Publish module to PowerShell Gallery using API key from environment.'
@@ -280,4 +290,4 @@ LogGroup 'Create GitHub release' {
280290
}
281291
#endregion Create GitHub release
282292

283-
Write-Host "Publishing complete. Version: [$releaseTag]"
293+
Write-Host "Publishing complete. PowerShell Gallery version: [$publishPSVersion]. GitHub release tag: [$releaseTag]."
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
2+
'PSUseDeclaredVarsMoreThanAssignments', '',
3+
Justification = 'Variables are assigned in BeforeAll and used inside It blocks.'
4+
)]
5+
[CmdletBinding()]
6+
param()
7+
8+
BeforeAll {
9+
Import-Module -Name 'PSModule' -Force
10+
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '../src/Publish-PSModule.Helpers.psm1') -Force
11+
}
12+
13+
Describe 'Publish-PSModule.Helpers' {
14+
Describe 'Get-ModuleVersionString' {
15+
Context 'Get-ModuleVersionString - SemVer only, never prefixed' {
16+
It 'Get-ModuleVersionString - returns the module version for a stable release' {
17+
Get-ModuleVersionString -ModuleVersion '1.1.10' | Should -Be '1.1.10'
18+
}
19+
20+
It 'Get-ModuleVersionString - appends the prerelease label' {
21+
Get-ModuleVersionString -ModuleVersion '1.1.10' -Prerelease 'mybranch001' |
22+
Should -Be '1.1.10-mybranch001'
23+
}
24+
25+
It 'Get-ModuleVersionString - treats an empty prerelease label as a stable release' {
26+
Get-ModuleVersionString -ModuleVersion '1.1.10' -Prerelease '' | Should -Be '1.1.10'
27+
}
28+
29+
It 'Get-ModuleVersionString - treats a whitespace-only prerelease label as a stable release' {
30+
Get-ModuleVersionString -ModuleVersion '1.1.10' -Prerelease ' ' | Should -Be '1.1.10'
31+
}
32+
33+
It 'Get-ModuleVersionString - trims whitespace around the prerelease label' {
34+
Get-ModuleVersionString -ModuleVersion '1.1.10' -Prerelease ' mybranch001 ' |
35+
Should -Be '1.1.10-mybranch001'
36+
}
37+
38+
It 'Get-ModuleVersionString - takes no version prefix parameter at all' {
39+
(Get-Command Get-ModuleVersionString).Parameters.Keys | Should -Not -Contain 'VersionPrefix'
40+
}
41+
42+
It 'Get-ModuleVersionString - requires a module version' {
43+
{ Get-ModuleVersionString -ModuleVersion '' } | Should -Throw
44+
}
45+
}
46+
}
47+
48+
Describe 'Get-ReleaseTag' {
49+
Context 'Get-ReleaseTag - repository with a version prefix' {
50+
It 'Get-ReleaseTag - prefixes a stable release tag with the configured prefix' {
51+
Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' | Should -Be 'v1.1.10'
52+
}
53+
54+
It 'Get-ReleaseTag - prefixes a stable release tag when the prerelease label is empty' {
55+
Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' -Prerelease '' | Should -Be 'v1.1.10'
56+
}
57+
58+
It 'Get-ReleaseTag - prefixes a prerelease tag with the configured prefix' {
59+
Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' -Prerelease 'mybranch001' |
60+
Should -Be 'v1.1.10-mybranch001'
61+
}
62+
63+
It 'Get-ReleaseTag - supports a multi-character prefix' {
64+
Get-ReleaseTag -VersionPrefix 'release-v' -ModuleVersion '2.0.0' | Should -Be 'release-v2.0.0'
65+
}
66+
}
67+
68+
Context 'Get-ReleaseTag - repository without a version prefix' {
69+
It 'Get-ReleaseTag - leaves a stable release tag unprefixed' {
70+
Get-ReleaseTag -VersionPrefix '' -ModuleVersion '1.1.10' | Should -Be '1.1.10'
71+
}
72+
73+
It 'Get-ReleaseTag - leaves a prerelease tag unprefixed' {
74+
Get-ReleaseTag -VersionPrefix '' -ModuleVersion '1.1.10' -Prerelease 'mybranch001' |
75+
Should -Be '1.1.10-mybranch001'
76+
}
77+
78+
It 'Get-ReleaseTag - treats an absent prefix as no prefix' {
79+
Get-ReleaseTag -ModuleVersion '1.1.10' | Should -Be '1.1.10'
80+
}
81+
82+
It 'Get-ReleaseTag - treats a null prefix as no prefix' {
83+
Get-ReleaseTag -VersionPrefix $null -ModuleVersion '1.1.10' | Should -Be '1.1.10'
84+
}
85+
}
86+
87+
Context 'Get-ReleaseTag - input normalization' {
88+
It 'Get-ReleaseTag - trims whitespace around the prefix' {
89+
Get-ReleaseTag -VersionPrefix ' v ' -ModuleVersion '1.1.10' | Should -Be 'v1.1.10'
90+
}
91+
92+
It 'Get-ReleaseTag - trims whitespace around the prerelease label' {
93+
Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' -Prerelease ' mybranch001 ' |
94+
Should -Be 'v1.1.10-mybranch001'
95+
}
96+
97+
It 'Get-ReleaseTag - treats a whitespace-only prerelease label as a stable release' {
98+
Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' -Prerelease ' ' | Should -Be 'v1.1.10'
99+
}
100+
101+
It 'Get-ReleaseTag - requires a module version' {
102+
{ Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '' } | Should -Throw
103+
}
104+
}
105+
106+
# Cleanup-PSModulePrereleases selects the releases to delete with
107+
# `tagName -like "*$prereleaseName*" -and tagName -ne $publishedReleaseTag`, where the published tag is
108+
# the value publish.ps1 exports as PSMODULE_PUBLISH_PSMODULE_CONTEXT_ReleaseTag. Both halves of that
109+
# filter have to keep working once the tag carries a prefix.
110+
Context 'Get-ReleaseTag - AutoCleanup tag matching contract' {
111+
It 'Get-ReleaseTag - keeps the prerelease name inside a prefixed tag so cleanup still matches it' {
112+
Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' -Prerelease 'mybranch001' |
113+
Should -BeLike '*mybranch*'
114+
}
115+
116+
It 'Get-ReleaseTag - keeps the prerelease name inside an unprefixed tag so cleanup still matches it' {
117+
Get-ReleaseTag -VersionPrefix '' -ModuleVersion '1.1.10' -Prerelease 'mybranch001' |
118+
Should -BeLike '*mybranch*'
119+
}
120+
121+
It 'Get-ReleaseTag - produces the same tag twice so cleanup can exclude the published release' {
122+
$first = Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' -Prerelease 'mybranch001'
123+
$second = Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' -Prerelease 'mybranch001'
124+
$first | Should -Be $second
125+
}
126+
}
127+
128+
# The PowerShell Gallery and the module manifest only accept plain SemVer. The prefix therefore
129+
# belongs to the GitHub release tag and to nothing else, and the two strings must differ by exactly
130+
# the prefix - never by anything else, and never in the other direction.
131+
Context 'Get-ReleaseTag - the prefix reaches the release tag and nothing else' {
132+
It 'Get-ReleaseTag - the tag is the prefix followed by the module version string' -ForEach @(
133+
@{ Prefix = 'v'; Version = '1.1.10'; Label = '' }
134+
@{ Prefix = 'v'; Version = '1.1.10'; Label = 'mybranch001' }
135+
@{ Prefix = ''; Version = '1.1.10'; Label = '' }
136+
@{ Prefix = ''; Version = '1.1.10'; Label = 'mybranch001' }
137+
@{ Prefix = 'release-v'; Version = '2.0.0'; Label = 'mybranch001' }
138+
) {
139+
$moduleVersion = Get-ModuleVersionString -ModuleVersion $Version -Prerelease $Label
140+
$tag = Get-ReleaseTag -VersionPrefix $Prefix -ModuleVersion $Version -Prerelease $Label
141+
$tag | Should -Be "$Prefix$moduleVersion"
142+
}
143+
144+
It 'Get-ReleaseTag - the module version string never gains the prefix' -ForEach @(
145+
@{ Prefix = 'v'; Version = '1.1.10'; Label = '' }
146+
@{ Prefix = 'v'; Version = '1.1.10'; Label = 'mybranch001' }
147+
@{ Prefix = 'release-v'; Version = '2.0.0'; Label = 'mybranch001' }
148+
) {
149+
$moduleVersion = Get-ModuleVersionString -ModuleVersion $Version -Prerelease $Label
150+
$moduleVersion | Should -Not -BeLike "$Prefix*"
151+
$moduleVersion | Should -Match '^\d+\.\d+\.\d+(-[0-9A-Za-z\-.]+)?$'
152+
}
153+
154+
It 'Get-ReleaseTag - an unprefixed repository gets identical strings' {
155+
$moduleVersion = Get-ModuleVersionString -ModuleVersion '1.1.10' -Prerelease 'mybranch001'
156+
$tag = Get-ReleaseTag -VersionPrefix '' -ModuleVersion '1.1.10' -Prerelease 'mybranch001'
157+
$tag | Should -Be $moduleVersion
158+
}
159+
160+
It 'Get-ReleaseTag - stripping the prefix from the tag yields the module version string' {
161+
$moduleVersion = Get-ModuleVersionString -ModuleVersion '1.1.10' -Prerelease 'mybranch001'
162+
$tag = Get-ReleaseTag -VersionPrefix 'v' -ModuleVersion '1.1.10' -Prerelease 'mybranch001'
163+
$tag -replace '^v' | Should -Be $moduleVersion
164+
}
165+
}
166+
}
167+
}

0 commit comments

Comments
 (0)