Skip to content

Restore the configured VersionPrefix on release tags created by Publish-PSModule #439

Description

Context and request

Observed behavior: since v6, Publish-PSModule derives the git tag from the compiled manifest's ModuleVersion instead of from the resolved FullVersion. ModuleVersion is Major.Minor.Patch by definition — the script even enforces ^\d+\.\d+\.\d+$ — so the configured VersionPrefix is dropped and the release is tagged without it.

.github/actions/Publish-PSModule/src/publish.ps1 (v6.1.15):

$moduleVersion = $manifestData.ModuleVersion
if (-not ($moduleVersion -match '^\d+\.\d+\.\d+$')) { ... }
...
$releaseTag = if ($createPrerelease) { "$moduleVersion-$prerelease" } else { $moduleVersion }
...
$releaseCreateCommand = @('release', 'create', $releaseTag)

The Plan job resolves the prefix correctly and passes it through. From the PSSemVer publish run for PR PSModule/PSSemVer#46:

VersionPrefix         : v
GitHub version: [v1.1.9]
New version: [v1.1.10]
"Resolution":{"Version":"1.1.10","Prerelease":"","FullVersion":"v1.1.10","ReleaseType":"Release","CreateRelease":true}

FullVersion is v1.1.10; the tag created is 1.1.10.

Expected behavior: the release tag is the resolved FullVersion, so VersionPrefix: 'v' keeps producing v1.1.10 and a repository's tag history stays consistent.

Reproduction: merge any release-labelled pull request in a module repository pinned to v6 whose settings leave VersionPrefix at the default v, then compare the new tag to the previous one.

Environment: Process-PSModule v6.0.0 through v6.1.15; any module repository using the default VersionPrefix: 'v'.

Regression: yes. v5 produced prefixed tags. Three repositories have already published on v6 and every one of them lost the prefix at the v6 boundary:

Repository Last v5 tag First v6 tag
PSModule/Toml v0.0.1 0.0.2
PSModule/Domeneshop v0.0.2 1.0.0, 1.0.1
PSModule/PSSemVer v1.1.9 1.1.10

This looks like a side effect of #326, which made publish read the version from the manifest read-only. The manifest is the right source for the module version, but it cannot carry the tag prefix.

Workaround: none that preserves history. Setting VersionPrefix: '' makes the repository consistent going forward but abandons every existing v-prefixed tag.

Acceptance criteria:

  • With VersionPrefix: 'v', a stable release is tagged v<Major>.<Minor>.<Patch> and a prerelease is tagged v<Major>.<Minor>.<Patch>-<label>.
  • With VersionPrefix: '', tags stay unprefixed.
  • AutoCleanup still matches and removes the prerelease tags it created, in both prefixed and unprefixed repositories.
  • A regression test covers a prefixed repository, so the prefix cannot be dropped silently again.

Technical decisions

The root cause is bounded to the tag derivation in .github/actions/Publish-PSModule/src/publish.ps1. The manifest check should stay — it is a valid guard that the artifact was stamped — but $releaseTag should come from the Plan job's FullVersion output (or from Version plus the settings VersionPrefix), not from $moduleVersion. The ModuleVersion validation and the 999.0.0 placeholder check remain unchanged.

Two decisions are needed:

  1. Whether the already-published unprefixed tags are corrected. Retagging PSSemVer 1.1.10, Toml 0.0.2, and Domeneshop 1.0.0/1.0.1 would restore consistency, but the releases and their attached artifacts are already public, and 1.1.10 is the tag the PowerShell Gallery listing links to. Leaving them and fixing forward is the lower-risk option; either way the choice should be explicit.
  2. Whether prefix consistency is enforced. Resolve-PSModuleVersion's Get-LatestGitHubVersion reads tagName and Get-LatestPublishedVersion sorts a prefixed and an unprefixed [PSSemVer] together — CompareTo ignores Prefix, so equal versions sort arbitrarily and the prefix can be lost from the resolved latest version. That is survivable because the prefix is reapplied from settings afterwards, but a repository with mixed tags is worth a warning.

Blast radius grows with #438: 48 of 58 module repositories are still on v5 and will hit this the first time they publish after migrating.

Implementation plan

Add a regression test that publishes from a fixture repository configured with VersionPrefix: 'v' and asserts the created tag, confirm it fails against current main, change $releaseTag to use the resolved full version, re-run the prefixed and unprefixed fixtures plus the AutoCleanup path, then record the decision on the already-published tags.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions