🪲 [Fix]: Manual dispatch on the default branch honours the merged pull request's version label - #531
Open
Marius Storhaug (MariusStorhaug) wants to merge 2 commits into
Open
🪲 [Fix]: Manual dispatch on the default branch honours the merged pull request's version label#531Marius Storhaug (MariusStorhaug) wants to merge 2 commits into
Marius Storhaug (MariusStorhaug) wants to merge 2 commits into
Conversation
A workflow_dispatch on the default branch is the documented recovery route for a failed or cancelled release run. Pull request association was gated on $isPush, so a dispatch resolved no pull request, saw no version label, and silently applied the AutoPatching patch fallback. Widen the gate to cover a manual dispatch on the default branch, so the same association and selection path runs for the same merge commit. Also fail loudly when a merged default-branch pull request is associated with the commit but does not match it, instead of falling back to a patch bump. A wrong version published to the PowerShell Gallery cannot be reclaimed. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Extract the gate, selection, and no-pull-request decision from main.ps1 into Resolve-ReleasePullRequest, with the GitHub lookup injected as a script block. The composed logic that produced the wrong version is now covered directly rather than re-implemented in a test. Add Get-DiscardedReleasePullRequest so a merged default-branch pull request that does not match the released commit fails the run instead of silently applying a patch bump. Open pull requests and non-default-branch merges are ignored, so a direct push and a feature-branch push are unaffected. Each new test was verified to fail against the specific defect it guards. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Marius Storhaug (MariusStorhaug)
marked this pull request as ready for review
September 2, 2026 09:36
Marius Storhaug (MariusStorhaug)
requested a review
from a team
as a code owner
September 2, 2026 09:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A manual dispatch on the default branch now resolves the merged pull request for the dispatched commit and applies its version label, instead of discarding the label and silently releasing a patch bump.
Fixed: a recovery dispatch releases the intended version
Manual dispatch on the default branch is the documented recovery route when a release run fails or is cancelled. In that exact scenario the label that determines the version bump was ignored, and the run published a version nobody asked for. A PowerShell Gallery version cannot be reclaimed once taken.
Validated on
MariusStorhaug/MariusTestModule. The same commit705564d, the merge commit of aMinor-labelled pull request, with the latest release atv0.4.13:v0.4.14v0.5.0A
pushof the same commit already resolvedv0.5.0, so a dispatch now behaves the same as the push it replaces.No repository configuration changes are needed. A workflow that previously resolved the wrong version on a recovery dispatch resolves the intended version on the next run.
A dispatch on a commit pushed directly to the default branch still releases the default patch bump, because there is no label to honour.
Added: a version label that cannot be determined now fails the run
When the commit being released belongs to a pull request that was merged into the default branch but is not that pull request's merge commit, the version label cannot be determined. The run now stops with an actionable message instead of assuming a patch bump:
A loud failure is strictly better than a silently wrong version that cannot be withdrawn.
Technical details
.github/actions/Get-PSModuleSettings/src/main.ps1— pull request association was gated onif ($isPush -and $commitSha). For aworkflow_dispatchthe gate was false even though both facts it needed were already true one line above:$commitShais populated fromGITHUB_SHAfor every non-push event, and$isManualDispatchToDefaultBranchwas already computed. The event was fully recognised; only the lookup was skipped. With$pullRequestleft null,Context.PullRequestserialised as null,Get-GitHubPullRequesttook itsIsPushToDefaultBranch -or IsManualDispatchToDefaultBranchbranch and returnedLabels = @()withIsDirectRelease = $true, andResolve-ReleaseDecisionthen set$patchReleasethrough$Configuration.AutoPatching -or $isDirectStableRelease. The label never reached the decision, so a recovery dispatch was indistinguishable from a direct push. The gate is now($isPush -or $IsManualDispatchToDefaultBranch).Select-PullRequestForPushneeded no change. The commit association endpoint returns the pull request for a squash-merge commit withbase.refequal to the default branch,merged_atset, andmerge_commit_shaexactly equal to the released commit, so all three of its criteria already hold. Confirmed against the live API for705564d(#64) and68696b6(🪲 [Fix]: New module versions publish to the PowerShell Gallery #529).Get-DiscardedReleasePullRequestreports only the second case.629ea31returning #64 while still open — so treating those as failures would break every direct default-branch push made while a pull request is open. Pushes to non-default branches pass through the same gate but have no release to get wrong, so the guard is scoped to$isPushToDefaultBranch -or $isManualDispatchToDefaultBranch..github/actions/Get-PSModuleSettings/src/Get-PSModuleSettings.Helpers.psm1— the gate, the selection, and the no-pull-request decision were inline inmain.ps1, which needs a large environment and a live API to run, so the composed logic that produced the wrong version could not be tested. They are nowResolve-ReleasePullRequest, with the GitHub lookup injected as a[scriptblock]. The behaviour is covered directly instead of being re-implemented in a test, which would have proved nothing about the shipped code. TheLogGroupboundary is safe here:Set-GitHubLogGroupdot-sources its script block, verified empirically, so assignments andthrowpropagate to the caller.@($response.Response)became@($response.Response | Where-Object { $null -ne $_ }).@($null)hasCount1, not 0, so an empty association response produced a single-element array containing$null.Select-PullRequestForPushtolerated it, but the new guard iterates the same collection and would otherwise inspect a null element. Covered by a test that returns$nullfrom the lookup..github/actions/Get-PSModuleSettings/tests/and.github/actions/Resolve-PSModuleVersion/tests/— ten new tests. Each was verified to fail against the specific defect it guards, by reverting each behaviour independently rather than by deleting the new functions, so the assertions are known to catch behaviour and not absence: narrowing the gate back to$isPushfails 3, disabling the no-pull-request guard fails 2, and both together fail 4. The version-action test asserts both versions the two possible dispatch contexts produce,v0.4.14andv0.5.0, so it pins the actual consequence rather than an intermediate flag.Set-Item function:global:Xteardown is not involved. Where a test needs to observe whether the injected lookup ran, it uses a hashtable captured byGetNewClosure()rather than a$script:flag, because a flag set inside a script block invoked with&does not propagate out. One authoring trap was hit and fixed during development:{ ... }.GetNewClosure()passed as an argument inside aShould -Throwscript block is evaluated in the wrong scope and does not capture, so the closure is now assigned to a variable first.Test-Actions.ymlbuilds, includingRun.ParallelandRun.Shuffle: 90 tests, 0 failures. PSScriptAnalyzer is clean against.github/linters/.powershell-psscriptanalyzer.psd1.WhatIf: trueon all three publish steps inPublish-Module.yml, so no Gallery version and no GitHub release could be created by any reproduction or validation run. Confirmed before triggering anything, and confirmed after: the Gallery and the latest release are both still0.4.13. Those branches are not part of this pull request, and are deleted once the fix ships..github/actions/Get-PSModuleSettings/src/**(PowerShell).github/actions/Get-PSModuleSettings/tests/**(Pester).github/actions/Resolve-PSModuleVersion/tests/**(Pester)Relevant issues (or links)
Related work