Skip to content

Commit 24854c9

Browse files
🧭 [Feature]: Normalize release context for push events
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 833a9b8 commit 24854c9

2 files changed

Lines changed: 102 additions & 25 deletions

File tree

.github/actions/Get-PSModuleSettings/src/Settings.schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@
142142
"type": "boolean",
143143
"description": "Automatically apply patches"
144144
},
145+
"AllowDirectPushRelease": {
146+
"type": "boolean",
147+
"description": "Allow a stable release from a default-branch push that has no associated pull request. Defaults to false."
148+
},
145149
"IncrementalPrerelease": {
146150
"type": "boolean",
147151
"description": "Use incremental prerelease versioning"

.github/actions/Get-PSModuleSettings/src/main.ps1

Lines changed: 98 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ $settings = [pscustomobject]@{
190190
Skip = $settings.Publish.Module.Skip ?? $false
191191
AutoCleanup = $settings.Publish.Module.AutoCleanup ?? $true
192192
AutoPatching = $settings.Publish.Module.AutoPatching ?? $true
193+
AllowDirectPushRelease = $settings.Publish.Module.AllowDirectPushRelease ?? $false
193194
IncrementalPrerelease = $settings.Publish.Module.IncrementalPrerelease ?? $true
194195
DatePrereleaseFormat = $settings.Publish.Module.DatePrereleaseFormat ?? ''
195196
VersionPrefix = $settings.Publish.Module.VersionPrefix ?? 'v'
@@ -226,43 +227,103 @@ LogGroup 'Calculate Job Run Conditions:' {
226227
$eventData | ConvertTo-Json -Depth 10 | Out-String
227228
}
228229

230+
$defaultBranch = $eventData.Repository.default_branch
231+
$eventName = $env:GITHUB_EVENT_NAME
232+
$isPR = $eventName -eq 'pull_request'
233+
$isPush = $eventName -eq 'push'
229234
$pullRequestAction = $eventData.Action
235+
$commitSha = if ($isPush) { $eventData.After ?? $env:GITHUB_SHA } else { $env:GITHUB_SHA }
236+
$pushBranch = if ($isPush) { $eventData.Ref -replace '^refs/heads/', '' } else { '' }
237+
$isPushToDefaultBranch = $isPush -and $pushBranch -eq $defaultBranch
230238
$pullRequest = $eventData.PullRequest
231-
$pullRequestIsMerged = $pullRequest.Merged
232-
$targetBranch = $pullRequest.Base.Ref
233-
$defaultBranch = $eventData.Repository.default_branch
239+
240+
if ($isPush -and $commitSha) {
241+
LogGroup "Resolve pull request for commit [$commitSha]" {
242+
$owner = $env:GITHUB_REPOSITORY_OWNER
243+
$repo = $env:GITHUB_REPOSITORY_NAME
244+
$response = Invoke-GitHubAPI -ApiEndpoint "/repos/$owner/$repo/commits/$commitSha/pulls" -Method GET
245+
$associatedPullRequests = @($response.Response)
246+
$pullRequest = $associatedPullRequests |
247+
Where-Object { $_.Base.Ref -eq $defaultBranch } |
248+
Sort-Object -Property @{ Expression = { $_.'merge_commit_sha' -eq $commitSha }; Descending = $true },
249+
@{ Expression = { $_.'merged_at' }; Descending = $true } |
250+
Select-Object -First 1
251+
252+
if ($pullRequest) {
253+
Write-Host "Resolved pull request #$($pullRequest.Number) from commit [$commitSha]."
254+
} else {
255+
Write-Host "::notice::No pull request is associated with commit [$commitSha]."
256+
}
257+
}
258+
}
259+
260+
$pullRequestIsMerged = if ($null -ne $pullRequest.Merged) {
261+
[bool]$pullRequest.Merged
262+
} else {
263+
-not [string]::IsNullOrWhiteSpace($pullRequest.'merged_at')
264+
}
265+
$targetBranch = if ($pullRequest) { $pullRequest.Base.Ref } elseif ($isPush) { $pushBranch } else { '' }
234266
$isTargetDefaultBranch = $targetBranch -eq $defaultBranch
267+
$pullRequestContext = if ($pullRequest) {
268+
[pscustomobject]@{
269+
Number = $pullRequest.Number
270+
Title = $pullRequest.Title
271+
Body = $pullRequest.Body
272+
HeadRef = $pullRequest.Head.Ref
273+
BaseRef = $pullRequest.Base.Ref
274+
Labels = @($pullRequest.Labels.Name)
275+
Merged = $pullRequestIsMerged
276+
MergeCommitSha = $pullRequest.'merge_commit_sha'
277+
HtmlUrl = $pullRequest.'html_url'
278+
}
279+
} else {
280+
$null
281+
}
282+
283+
$settings | Add-Member -MemberType NoteProperty -Name Context -Value ([pscustomobject]@{
284+
EventName = $eventName
285+
EventAction = $pullRequestAction
286+
CommitSha = $commitSha
287+
Ref = if ($isPush) { $eventData.Ref } else { $env:GITHUB_REF }
288+
DefaultBranch = $defaultBranch
289+
IsPushToDefaultBranch = $isPushToDefaultBranch
290+
PullRequest = $pullRequestContext
291+
}) -Force
235292

236293
Write-Host 'GitHub event inputs:'
237294
[pscustomobject]@{
238-
GITHUB_EVENT_NAME = $env:GITHUB_EVENT_NAME
295+
GITHUB_EVENT_NAME = $eventName
239296
GITHUB_EVENT_ACTION = $pullRequestAction
240297
GITHUB_EVENT_PULL_REQUEST_MERGED = $pullRequestIsMerged
298+
CommitSha = $commitSha
299+
PushBranch = $pushBranch
241300
TargetBranch = $targetBranch
242301
DefaultBranch = $defaultBranch
243302
IsTargetDefaultBranch = $isTargetDefaultBranch
303+
IsPushToDefaultBranch = $isPushToDefaultBranch
304+
AssociatedPullRequest = $pullRequestContext.Number
244305
} | Format-List | Out-String
245306

246-
$isPR = $env:GITHUB_EVENT_NAME -eq 'pull_request'
247307
$isOpenOrUpdatedPR = $isPR -and $pullRequestAction -in @('opened', 'reopened', 'synchronize', 'labeled', 'unlabeled')
308+
$isClosedPR = $isPR -and $pullRequestAction -eq 'closed'
248309
$isAbandonedPR = $isPR -and $pullRequestAction -eq 'closed' -and $pullRequestIsMerged -ne $true
249310
$isMergedPR = $isPR -and $pullRequestAction -eq 'closed' -and $pullRequestIsMerged -eq $true
250-
$isNotAbandonedPR = -not $isAbandonedPR
311+
$hasPullRequestContext = $null -ne $pullRequestContext
251312

252313
# Check if a prerelease label exists on the PR
253314
$prereleaseLabels = $settings.Publish.Module.PrereleaseLabels -split ',' | ForEach-Object { $_.Trim() }
254-
$prLabels = @($pullRequest.labels.name)
315+
$prLabels = @($pullRequestContext.Labels)
255316
$hasPrereleaseLabel = ($prLabels | Where-Object { $prereleaseLabels -contains $_ }).Count -gt 0
256317
$isOpenOrLabeledPR = $isPR -and $pullRequestAction -in @('opened', 'reopened', 'synchronize', 'labeled')
257318

258319
# Check if important files have changed in the PR
259320
# Important files are determined by the configured ImportantFilePatterns setting
260321
$hasImportantChanges = $false
261-
if ($isPR -and $pullRequest.Number) {
322+
if ($pullRequestContext.Number) {
262323
LogGroup 'Check for Important File Changes' {
263324
$owner = $env:GITHUB_REPOSITORY_OWNER
264325
$repo = $env:GITHUB_REPOSITORY_NAME
265-
$prNumber = $pullRequest.Number
326+
$prNumber = $pullRequestContext.Number
266327

267328
Write-Host "Fetching changed files for PR #$prNumber..."
268329
$changedFiles = Invoke-GitHubAPI -ApiEndpoint "/repos/$owner/$repo/pulls/$prNumber/files" -Method GET |
@@ -332,10 +393,17 @@ If you believe this is incorrect, please verify that your changes are in the cor
332393
}
333394
}
334395
}
396+
} elseif ($isPush) {
397+
$hasImportantChanges = $isPushToDefaultBranch -and $settings.Publish.Module.AllowDirectPushRelease
398+
if ($hasImportantChanges) {
399+
Write-Host 'Direct push release is explicitly enabled; treating the push as having important changes.'
400+
} else {
401+
Write-Host 'Push has no associated pull request; stable publishing is disabled by default.'
402+
}
335403
} else {
336-
# Not a PR event or no PR number - consider as having important changes (e.g., workflow_dispatch, schedule)
404+
# Preserve build/test behavior for workflow_dispatch and schedule events.
337405
$hasImportantChanges = $true
338-
Write-Host 'Not a PR event or missing PR number - treating as having important changes'
406+
Write-Host 'Non-PR event - treating as having important changes'
339407
}
340408

341409
# Prerelease requires both: prerelease label AND important file changes
@@ -344,9 +412,12 @@ If you believe this is incorrect, please verify that your changes are in the cor
344412

345413
# Determine ReleaseType - what type of release to create
346414
# Values: 'Release', 'Prerelease', 'None'
347-
# Release only happens when important files changed (actual module code/docs)
348-
# Merged PRs without important changes should only trigger cleanup, not a new release
349-
$releaseType = if ($isMergedPR -and $isTargetDefaultBranch -and $hasImportantChanges) {
415+
# Stable releases are push-driven. A direct push requires explicit opt-in.
416+
$releaseType = if (
417+
$isPushToDefaultBranch -and
418+
$hasImportantChanges -and
419+
($hasPullRequestContext -or $settings.Publish.Module.AllowDirectPushRelease)
420+
) {
350421
'Release'
351422
} elseif ($shouldPrerelease) {
352423
'Prerelease'
@@ -358,10 +429,13 @@ If you believe this is incorrect, please verify that your changes are in the cor
358429
isPR = $isPR
359430
isOpenOrUpdatedPR = $isOpenOrUpdatedPR
360431
isOpenOrLabeledPR = $isOpenOrLabeledPR
432+
isClosedPR = $isClosedPR
361433
isAbandonedPR = $isAbandonedPR
362434
isMergedPR = $isMergedPR
363-
isNotAbandonedPR = $isNotAbandonedPR
435+
isPush = $isPush
436+
isPushToDefaultBranch = $isPushToDefaultBranch
364437
isTargetDefaultBranch = $isTargetDefaultBranch
438+
hasPullRequestContext = $hasPullRequestContext
365439
hasPrereleaseLabel = $hasPrereleaseLabel
366440
shouldPrerelease = $shouldPrerelease
367441
ReleaseType = $releaseType
@@ -532,23 +606,22 @@ $settings.Test.Module | Add-Member -MemberType NoteProperty -Name Suites -Value
532606
# Calculate job-specific conditions and add to settings
533607
LogGroup 'Calculate Job Run Conditions:' {
534608
# Calculate if prereleases should be cleaned up:
535-
# True if (Release, merged PR to default branch, or Abandoned PR) AND user has AutoCleanup enabled (defaults to true)
536-
# Even if no important files changed, we still want to cleanup prereleases when merging to default branch
537-
$isReleaseOrMergedOrAbandoned = (
609+
# Closed PRs only clean up prereleases. Push runs also clean up the associated PR channel.
610+
$shouldCleanupEvent = (
538611
($releaseType -eq 'Release') -or
539-
($isMergedPR -and $isTargetDefaultBranch) -or
540-
$isAbandonedPR
612+
$isClosedPR -or
613+
($isPushToDefaultBranch -and $hasPullRequestContext)
541614
)
542-
$shouldAutoCleanup = $isReleaseOrMergedOrAbandoned -and ($settings.Publish.Module.AutoCleanup -eq $true)
615+
$shouldAutoCleanup = $shouldCleanupEvent -and ($settings.Publish.Module.AutoCleanup -eq $true)
543616

544617
# Update Publish.Module with computed release values
545618
$settings.Publish.Module | Add-Member -MemberType NoteProperty -Name ReleaseType -Value $releaseType -Force
546619
$settings.Publish.Module.AutoCleanup = $shouldAutoCleanup
547620

548621
# For open PRs, we only want to run build/test stages if important files changed.
549-
# For merged PRs, workflow_dispatch, schedule - $hasImportantChanges is already true.
622+
# Closed PR events are cleanup-only. workflow_dispatch and schedule retain build/test behavior.
550623
# Note: $shouldPrerelease already requires $hasImportantChanges, so no separate check needed.
551-
$shouldRunBuildTest = $isNotAbandonedPR -and $hasImportantChanges
624+
$shouldRunBuildTest = (-not $isClosedPR) -and $hasImportantChanges
552625

553626
# Check if setup/teardown scripts exist in the repository
554627
$hasBeforeAllScript = Test-Path -Path 'tests/BeforeAll.ps1'
@@ -607,8 +680,8 @@ LogGroup 'Calculate Job Run Conditions:' {
607680
$settings.Publish.Module | Add-Member -MemberType NoteProperty -Name Desired -Value (($releaseType -ne 'None') -or $shouldAutoCleanup) -Force
608681
$settings.Publish.Module | Add-Member -MemberType NoteProperty -Name Enabled -Value (($releaseType -ne 'None') -or $shouldAutoCleanup) -Force
609682
$settings.Publish | Add-Member -MemberType NoteProperty -Name Site -Value ([pscustomobject]@{
610-
Desired = $isMergedPR -and $isTargetDefaultBranch -and $hasImportantChanges
611-
Enabled = $isMergedPR -and $isTargetDefaultBranch -and $hasImportantChanges
683+
Desired = $releaseType -eq 'Release'
684+
Enabled = $releaseType -eq 'Release'
612685
}) -Force
613686

614687
$settings | Add-Member -MemberType NoteProperty -Name HasImportantChanges -Value $hasImportantChanges

0 commit comments

Comments
 (0)