Skip to content

Commit 94674b7

Browse files
Normalize release event context
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 68a2a7f commit 94674b7

6 files changed

Lines changed: 555 additions & 78 deletions

File tree

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
function Resolve-WorkflowEventRouting {
2+
<#
3+
.SYNOPSIS
4+
Resolves release and execution routing from normalized GitHub event state.
5+
#>
6+
[CmdletBinding()]
7+
[OutputType([PSCustomObject])]
8+
param(
9+
[Parameter(Mandatory)]
10+
[string] $EventName,
11+
12+
[Parameter()]
13+
[string] $EventAction,
14+
15+
[Parameter()]
16+
[bool] $PullRequestIsMerged,
17+
18+
[Parameter()]
19+
[bool] $IsTargetDefaultBranch,
20+
21+
[Parameter()]
22+
[bool] $IsPushToDefaultBranch,
23+
24+
[Parameter()]
25+
[bool] $IsManualDispatchToDefaultBranch,
26+
27+
[Parameter()]
28+
[bool] $HasImportantChanges,
29+
30+
[Parameter()]
31+
[bool] $HasPrereleaseLabel
32+
)
33+
34+
$isPR = $EventName -eq 'pull_request'
35+
$isPush = $EventName -eq 'push'
36+
$isManualDispatch = $EventName -eq 'workflow_dispatch'
37+
$isOpenOrUpdatedPR = $isPR -and $EventAction -in @('opened', 'reopened', 'synchronize', 'labeled', 'unlabeled')
38+
$isOpenOrLabeledPR = $isPR -and $EventAction -in @('opened', 'reopened', 'synchronize', 'labeled')
39+
$isClosedPR = $isPR -and $EventAction -eq 'closed'
40+
$isAbandonedPR = $isClosedPR -and -not $PullRequestIsMerged
41+
$isMergedPR = $isClosedPR -and $PullRequestIsMerged
42+
$shouldPrerelease = $isOpenOrLabeledPR -and $HasPrereleaseLabel -and $HasImportantChanges
43+
$shouldRelease = (
44+
($IsPushToDefaultBranch -or $IsManualDispatchToDefaultBranch) -and
45+
$HasImportantChanges
46+
)
47+
48+
[pscustomobject]@{
49+
IsPR = $isPR
50+
IsPush = $isPush
51+
IsManualDispatch = $isManualDispatch
52+
IsOpenOrUpdatedPR = $isOpenOrUpdatedPR
53+
IsOpenOrLabeledPR = $isOpenOrLabeledPR
54+
IsClosedPR = $isClosedPR
55+
IsAbandonedPR = $isAbandonedPR
56+
IsMergedPR = $isMergedPR
57+
IsTargetDefaultBranch = $IsTargetDefaultBranch
58+
IsPushToDefaultBranch = $IsPushToDefaultBranch
59+
IsManualDispatchToDefaultBranch = $IsManualDispatchToDefaultBranch
60+
ShouldPrerelease = $shouldPrerelease
61+
ReleaseType = if ($shouldRelease) {
62+
'Release'
63+
} elseif ($shouldPrerelease) {
64+
'Prerelease'
65+
} else {
66+
'None'
67+
}
68+
ShouldRunBuildTest = (-not $isClosedPR) -and $HasImportantChanges
69+
ShouldCleanupEvent = $isClosedPR
70+
}
71+
}
72+
73+
function Select-PullRequestForPush {
74+
<#
75+
.SYNOPSIS
76+
Selects the merged default-branch pull request associated with a pushed commit.
77+
#>
78+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '',
79+
Justification = 'Parameter is used inside a Sort-Object script block.')]
80+
[CmdletBinding()]
81+
[OutputType([PSCustomObject])]
82+
param(
83+
[Parameter()]
84+
[object[]] $PullRequest,
85+
86+
[Parameter(Mandatory)]
87+
[string] $DefaultBranch,
88+
89+
[Parameter(Mandatory)]
90+
[string] $CommitSha
91+
)
92+
93+
$PullRequest |
94+
Where-Object {
95+
$_.Base.Ref -eq $DefaultBranch -and
96+
-not [string]::IsNullOrWhiteSpace($_.merged_at) -and
97+
$_.merge_commit_sha -eq $CommitSha
98+
} |
99+
Sort-Object -Property @{ Expression = { $_.merged_at }; Descending = $true } |
100+
Select-Object -First 1
101+
}
102+
103+
function Get-FilesFromGitTree {
104+
<#
105+
.SYNOPSIS
106+
Returns the files contained in a complete Git tree response.
107+
#>
108+
[CmdletBinding()]
109+
[OutputType([string[]])]
110+
param(
111+
[Parameter(Mandatory)]
112+
[PSCustomObject] $Tree
113+
)
114+
115+
if ($Tree.truncated) {
116+
throw 'Cannot determine changed files because the Git tree response was truncated.'
117+
}
118+
119+
@($Tree.tree |
120+
Where-Object { $_.type -eq 'blob' } |
121+
Select-Object -ExpandProperty path)
122+
}

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

Lines changed: 136 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
'powershell-yaml', 'Hashtable' | Install-PSResource -Repository PSGallery -TrustRepository
2+
Import-Module -Name "$PSScriptRoot/Get-PSModuleSettings.Helpers.psm1" -Force
23

34
$name = $env:PSMODULE_GET_SETTINGS_INPUT_Name
45
$settingsPath = $env:PSMODULE_GET_SETTINGS_INPUT_SettingsPath
@@ -226,43 +227,100 @@ LogGroup 'Calculate Job Run Conditions:' {
226227
$eventData | ConvertTo-Json -Depth 10 | Out-String
227228
}
228229

230+
$eventName = $env:GITHUB_EVENT_NAME
231+
$isPush = $eventName -eq 'push'
232+
$isManualDispatch = $eventName -eq 'workflow_dispatch'
233+
$defaultBranch = $eventData.Repository.default_branch
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+
$workflowRef = if ($isPush) { $pushBranch } else { $env:GITHUB_REF_NAME }
238+
$isPushToDefaultBranch = $isPush -and $pushBranch -eq $defaultBranch
239+
$isManualDispatchToDefaultBranch = $isManualDispatch -and $workflowRef -eq $defaultBranch
230240
$pullRequest = $eventData.PullRequest
231-
$pullRequestIsMerged = $pullRequest.Merged
232-
$targetBranch = $pullRequest.Base.Ref
233-
$defaultBranch = $eventData.Repository.default_branch
241+
242+
if ($isPush -and $commitSha) {
243+
LogGroup "Resolve pull request for commit [$commitSha]" {
244+
$owner = $env:GITHUB_REPOSITORY_OWNER
245+
$repo = $env:GITHUB_REPOSITORY_NAME
246+
$response = Invoke-GitHubAPI -ApiEndpoint "/repos/$owner/$repo/commits/$commitSha/pulls" -Method GET
247+
$associatedPullRequests = @($response.Response)
248+
$pullRequest = Select-PullRequestForPush -PullRequest $associatedPullRequests `
249+
-DefaultBranch $defaultBranch `
250+
-CommitSha $commitSha
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 -eq $pullRequest) {
261+
$false
262+
} elseif ($null -ne $pullRequest.Merged) {
263+
[bool]$pullRequest.Merged
264+
} else {
265+
-not [string]::IsNullOrWhiteSpace($pullRequest.merged_at)
266+
}
267+
$targetBranch = if ($pullRequest) { $pullRequest.Base.Ref } elseif ($isPush) { $pushBranch } else { $workflowRef }
234268
$isTargetDefaultBranch = $targetBranch -eq $defaultBranch
269+
$pullRequestContext = if ($pullRequest) {
270+
[pscustomobject]@{
271+
Number = $pullRequest.Number
272+
Title = $pullRequest.Title
273+
Body = $pullRequest.Body
274+
HeadRef = $pullRequest.Head.Ref
275+
BaseRef = $pullRequest.Base.Ref
276+
Labels = @($pullRequest.Labels.Name)
277+
Merged = $pullRequestIsMerged
278+
MergeCommitSha = $pullRequest.merge_commit_sha
279+
HtmlUrl = $pullRequest.html_url
280+
}
281+
} else {
282+
$null
283+
}
284+
285+
$settings | Add-Member -MemberType NoteProperty -Name Context -Value ([pscustomobject]@{
286+
EventName = $eventName
287+
EventAction = $pullRequestAction
288+
CommitSha = $commitSha
289+
Ref = if ($isPush) { $eventData.Ref } else { $env:GITHUB_REF }
290+
DefaultBranch = $defaultBranch
291+
IsPushToDefaultBranch = $isPushToDefaultBranch
292+
IsManualDispatchToDefaultBranch = $isManualDispatchToDefaultBranch
293+
PullRequest = $pullRequestContext
294+
}) -Force
235295

236296
Write-Host 'GitHub event inputs:'
237297
[pscustomobject]@{
238-
GITHUB_EVENT_NAME = $env:GITHUB_EVENT_NAME
298+
GITHUB_EVENT_NAME = $eventName
239299
GITHUB_EVENT_ACTION = $pullRequestAction
240300
GITHUB_EVENT_PULL_REQUEST_MERGED = $pullRequestIsMerged
301+
CommitSha = $commitSha
302+
PushBranch = $pushBranch
241303
TargetBranch = $targetBranch
242304
DefaultBranch = $defaultBranch
243305
IsTargetDefaultBranch = $isTargetDefaultBranch
306+
IsPushToDefaultBranch = $isPushToDefaultBranch
307+
IsManualDispatchToDefaultBranch = $isManualDispatchToDefaultBranch
308+
AssociatedPullRequest = $pullRequestContext.Number
244309
} | Format-List | Out-String
245310

246-
$isPR = $env:GITHUB_EVENT_NAME -eq 'pull_request'
247-
$isOpenOrUpdatedPR = $isPR -and $pullRequestAction -in @('opened', 'reopened', 'synchronize', 'labeled', 'unlabeled')
248-
$isAbandonedPR = $isPR -and $pullRequestAction -eq 'closed' -and $pullRequestIsMerged -ne $true
249-
$isMergedPR = $isPR -and $pullRequestAction -eq 'closed' -and $pullRequestIsMerged -eq $true
250-
$isNotAbandonedPR = -not $isAbandonedPR
251-
252311
# Check if a prerelease label exists on the PR
253312
$prereleaseLabels = $settings.Publish.Module.PrereleaseLabels -split ',' | ForEach-Object { $_.Trim() }
254-
$prLabels = @($pullRequest.labels.name)
313+
$prLabels = @($pullRequestContext.Labels)
255314
$hasPrereleaseLabel = ($prLabels | Where-Object { $prereleaseLabels -contains $_ }).Count -gt 0
256-
$isOpenOrLabeledPR = $isPR -and $pullRequestAction -in @('opened', 'reopened', 'synchronize', 'labeled')
257315

258316
# Check if important files have changed in the PR
259317
# Important files are determined by the configured ImportantFilePatterns setting
260318
$hasImportantChanges = $false
261-
if ($isPR -and $pullRequest.Number) {
319+
if ($pullRequestContext.Number) {
262320
LogGroup 'Check for Important File Changes' {
263321
$owner = $env:GITHUB_REPOSITORY_OWNER
264322
$repo = $env:GITHUB_REPOSITORY_NAME
265-
$prNumber = $pullRequest.Number
323+
$prNumber = $pullRequestContext.Number
266324

267325
Write-Host "Fetching changed files for PR #$prNumber..."
268326
$changedFiles = Invoke-GitHubAPI -ApiEndpoint "/repos/$owner/$repo/pulls/$prNumber/files" -Method GET |
@@ -332,38 +390,72 @@ If you believe this is incorrect, please verify that your changes are in the cor
332390
}
333391
}
334392
}
393+
} elseif ($isPushToDefaultBranch) {
394+
LogGroup 'Check for Important File Changes' {
395+
$beforeCommitSha = $eventData.Before
396+
$owner = $env:GITHUB_REPOSITORY_OWNER
397+
$repo = $env:GITHUB_REPOSITORY_NAME
398+
if ([string]::IsNullOrWhiteSpace($beforeCommitSha) -or $beforeCommitSha -match '^0+$') {
399+
Write-Host "Fetching files for the initial push commit [$commitSha]..."
400+
$commit = (Invoke-GitHubAPI -ApiEndpoint "/repos/$owner/$repo/git/commits/$commitSha" -Method GET).Response
401+
$treeSha = $commit.tree.sha
402+
if ([string]::IsNullOrWhiteSpace($treeSha)) {
403+
throw "Cannot determine changed files because commit [$commitSha] has no tree."
404+
}
405+
406+
$tree = (Invoke-GitHubAPI -ApiEndpoint "/repos/$owner/$repo/git/trees/$treeSha?recursive=1" -Method GET).Response
407+
$changedFiles = Get-FilesFromGitTree -Tree $tree
408+
} else {
409+
Write-Host "Fetching changed files between [$beforeCommitSha] and [$commitSha]..."
410+
$changedFiles = Invoke-GitHubAPI -ApiEndpoint "/repos/$owner/$repo/compare/$beforeCommitSha...$commitSha" -Method GET |
411+
Select-Object -ExpandProperty Response |
412+
Select-Object -ExpandProperty files |
413+
Select-Object -ExpandProperty filename
414+
}
415+
416+
Write-Host "Changed files ($($changedFiles.Count)):"
417+
$changedFiles | ForEach-Object { Write-Host " - $_" }
418+
419+
foreach ($file in $changedFiles) {
420+
foreach ($pattern in $settings.ImportantFilePatterns) {
421+
if ($file -match $pattern) {
422+
$hasImportantChanges = $true
423+
Write-Host "Important file changed: [$file] (matches pattern: $pattern)"
424+
break
425+
}
426+
}
427+
if ($hasImportantChanges) { break }
428+
}
429+
}
335430
} else {
336-
# Not a PR event or no PR number - consider as having important changes (e.g., workflow_dispatch, schedule)
431+
# Manual dispatch and schedule runs retain their existing build/test behavior.
337432
$hasImportantChanges = $true
338-
Write-Host 'Not a PR event or missing PR number - treating as having important changes'
433+
Write-Host 'Non-PR event - treating as having important changes'
339434
}
340435

341-
# Prerelease requires both: prerelease label AND important file changes
342-
# No point creating a prerelease if only non-module files changed
343-
$shouldPrerelease = $isOpenOrLabeledPR -and $hasPrereleaseLabel -and $hasImportantChanges
344-
345-
# Determine ReleaseType - what type of release to create
346-
# 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) {
350-
'Release'
351-
} elseif ($shouldPrerelease) {
352-
'Prerelease'
353-
} else {
354-
'None'
355-
}
436+
$routing = Resolve-WorkflowEventRouting -EventName $eventName `
437+
-EventAction $pullRequestAction `
438+
-PullRequestIsMerged $pullRequestIsMerged `
439+
-IsTargetDefaultBranch $isTargetDefaultBranch `
440+
-IsPushToDefaultBranch $isPushToDefaultBranch `
441+
-IsManualDispatchToDefaultBranch $isManualDispatchToDefaultBranch `
442+
-HasImportantChanges $hasImportantChanges `
443+
-HasPrereleaseLabel $hasPrereleaseLabel
444+
$releaseType = $routing.ReleaseType
356445

357446
[pscustomobject]@{
358-
isPR = $isPR
359-
isOpenOrUpdatedPR = $isOpenOrUpdatedPR
360-
isOpenOrLabeledPR = $isOpenOrLabeledPR
361-
isAbandonedPR = $isAbandonedPR
362-
isMergedPR = $isMergedPR
363-
isNotAbandonedPR = $isNotAbandonedPR
364-
isTargetDefaultBranch = $isTargetDefaultBranch
447+
isPR = $routing.IsPR
448+
isOpenOrUpdatedPR = $routing.IsOpenOrUpdatedPR
449+
isOpenOrLabeledPR = $routing.IsOpenOrLabeledPR
450+
isClosedPR = $routing.IsClosedPR
451+
isAbandonedPR = $routing.IsAbandonedPR
452+
isMergedPR = $routing.IsMergedPR
453+
isPush = $routing.IsPush
454+
isManualDispatch = $routing.IsManualDispatch
455+
isPushToDefaultBranch = $routing.IsPushToDefaultBranch
456+
isTargetDefaultBranch = $routing.IsTargetDefaultBranch
365457
hasPrereleaseLabel = $hasPrereleaseLabel
366-
shouldPrerelease = $shouldPrerelease
458+
shouldPrerelease = $routing.ShouldPrerelease
367459
ReleaseType = $releaseType
368460
HasImportantChanges = $hasImportantChanges
369461
} | Format-List | Out-String
@@ -531,24 +623,14 @@ $settings.Test.Module | Add-Member -MemberType NoteProperty -Name Suites -Value
531623

532624
# Calculate job-specific conditions and add to settings
533625
LogGroup 'Calculate Job Run Conditions:' {
534-
# 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 = (
538-
($releaseType -eq 'Release') -or
539-
($isMergedPR -and $isTargetDefaultBranch) -or
540-
$isAbandonedPR
541-
)
542-
$shouldAutoCleanup = $isReleaseOrMergedOrAbandoned -and ($settings.Publish.Module.AutoCleanup -eq $true)
626+
$shouldAutoCleanup = $routing.ShouldCleanupEvent -and ($settings.Publish.Module.AutoCleanup -eq $true)
543627

544628
# Update Publish.Module with computed release values
545629
$settings.Publish.Module | Add-Member -MemberType NoteProperty -Name ReleaseType -Value $releaseType -Force
546630
$settings.Publish.Module.AutoCleanup = $shouldAutoCleanup
547631

548-
# 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.
550-
# Note: $shouldPrerelease already requires $hasImportantChanges, so no separate check needed.
551-
$shouldRunBuildTest = $isNotAbandonedPR -and $hasImportantChanges
632+
# Closed PR events are cleanup-only. Other events run build/test only for important changes.
633+
$shouldRunBuildTest = $routing.ShouldRunBuildTest
552634

553635
# Check if setup/teardown scripts exist in the repository
554636
$hasBeforeAllScript = Test-Path -Path 'tests/BeforeAll.ps1'
@@ -607,8 +689,8 @@ LogGroup 'Calculate Job Run Conditions:' {
607689
$settings.Publish.Module | Add-Member -MemberType NoteProperty -Name Desired -Value (($releaseType -ne 'None') -or $shouldAutoCleanup) -Force
608690
$settings.Publish.Module | Add-Member -MemberType NoteProperty -Name Enabled -Value (($releaseType -ne 'None') -or $shouldAutoCleanup) -Force
609691
$settings.Publish | Add-Member -MemberType NoteProperty -Name Site -Value ([pscustomobject]@{
610-
Desired = $isMergedPR -and $isTargetDefaultBranch -and $hasImportantChanges
611-
Enabled = $isMergedPR -and $isTargetDefaultBranch -and $hasImportantChanges
692+
Desired = $releaseType -eq 'Release'
693+
Enabled = $releaseType -eq 'Release'
612694
}) -Force
613695

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

0 commit comments

Comments
 (0)