From e37d99d219a2b0b641fccd5f7a75fd626465090e Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 3 Nov 2026 00:07:00 +0100 Subject: [PATCH 1/3] Add regression tests for branch prerelease cleanup --- tests/ReleaseCleanup.Tests.ps1 | 278 +++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 tests/ReleaseCleanup.Tests.ps1 diff --git a/tests/ReleaseCleanup.Tests.ps1 b/tests/ReleaseCleanup.Tests.ps1 new file mode 100644 index 0000000..38620b9 --- /dev/null +++ b/tests/ReleaseCleanup.Tests.ps1 @@ -0,0 +1,278 @@ +#Requires -Version 7.0 +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '6.0.0'; MaximumVersion = '6.*' } +#Requires -Modules PSSemVer + +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Variables are used by Pester test blocks.' +)] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSAvoidGlobalVars', '', Justification = 'Native command mocks share fixture state across script scopes and restore it after tests.' +)] +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' + +BeforeAll { + Import-Module -Name PSSemVer + $script:mainPath = Join-Path -Path $PSScriptRoot -ChildPath '..\src\main.ps1' + $script:previousExitCode = $global:LASTEXITCODE + $script:previousTestState = $global:ReleaseCleanupTestState + $global:ReleaseCleanupTestState = @{} + $script:previousEnvironment = @{} + $script:inputDefaults = @{ + GITHUB_EVENT_PATH = Join-Path -Path $TestDrive -ChildPath 'event.json' + PSMODULE_AUTO_RELEASE_INPUT_ConfigurationFile = Join-Path -Path $TestDrive -ChildPath 'missing.yml' + PSMODULE_AUTO_RELEASE_INPUT_AutoCleanup = 'true' + PSMODULE_AUTO_RELEASE_INPUT_AutoPatching = 'true' + PSMODULE_AUTO_RELEASE_INPUT_CreateMajorTag = 'true' + PSMODULE_AUTO_RELEASE_INPUT_CreateMinorTag = 'true' + PSMODULE_AUTO_RELEASE_INPUT_DatePrereleaseFormat = '' + PSMODULE_AUTO_RELEASE_INPUT_IgnoreLabels = 'NoRelease' + PSMODULE_AUTO_RELEASE_INPUT_IncrementalPrerelease = 'true' + PSMODULE_AUTO_RELEASE_INPUT_MajorLabels = 'major, breaking' + PSMODULE_AUTO_RELEASE_INPUT_MinorLabels = 'minor, feature' + PSMODULE_AUTO_RELEASE_INPUT_PatchLabels = 'patch, fix' + PSMODULE_AUTO_RELEASE_INPUT_UsePRBodyAsReleaseNotes = 'true' + PSMODULE_AUTO_RELEASE_INPUT_UsePRTitleAsReleaseName = 'false' + PSMODULE_AUTO_RELEASE_INPUT_UsePRTitleAsNotesHeading = 'true' + PSMODULE_AUTO_RELEASE_INPUT_VersionPrefix = 'v' + PSMODULE_AUTO_RELEASE_INPUT_WhatIf = 'false' + } + foreach ($name in $script:inputDefaults.Keys) { + $script:previousEnvironment[$name] = [Environment]::GetEnvironmentVariable($name) + } + + function Invoke-TestLogGroup { + <# + .SYNOPSIS + Run an action log group in its shared script context. + + .DESCRIPTION + Preserve the action's shared variables without depending on the GitHub logging runtime. + This adapter does not validate the external logging integration. + + .EXAMPLE + Invoke-TestLogGroup -Name 'Example' -ScriptBlock { $value = 1 } + Make the group's value available to the calling script. + #> + [Alias('LogGroup')] + [CmdletBinding()] + param( + # The log group name. + [Parameter(Mandatory, Position = 0)] + [string] $Name, + + # The action code to execute. + [Parameter(Mandatory, Position = 1)] + [scriptblock] $ScriptBlock + ) + + Write-Verbose -Message "Running test log group [$Name]." + $initialVariables = @(Get-Variable -Scope Local | Select-Object -ExpandProperty Name) + . $ScriptBlock + foreach ($variable in Get-Variable -Scope Local) { + if ($variable.Name -notin $initialVariables -and $variable.Name -notin @('initialVariables', 'variable')) { + Set-Variable -Name $variable.Name -Value $variable.Value -Scope 1 + } + } + } + + function Write-GitHubNotice { + <# + .SYNOPSIS + Accept a simulated action notice. + + .DESCRIPTION + Avoid depending on the GitHub logging runtime in local tests. + + .EXAMPLE + Write-GitHubNotice -Title 'Release created' -Message 'v1.2.4' + Accept the action's notice without writing to GitHub. + #> + [CmdletBinding()] + param( + # The notice title. + [Parameter()] + [string] $Title, + + # The notice message. + [Parameter()] + [string] $Message + ) + + Write-Verbose -Message "$Title`: $Message" + } +} + +Describe 'Branch prerelease cleanup' { + BeforeEach { + foreach ($name in $script:inputDefaults.Keys) { + [Environment]::SetEnvironmentVariable($name, $script:inputDefaults[$name]) + } + $script:event = @{ + action = 'closed' + pull_request = @{ + merged = $false + state = 'closed' + head = @{ ref = 'feature/test' } + base = @{ ref = 'main' } + labels = @() + number = 42 + title = 'Test release' + body = 'Test release notes' + } + } + $global:ReleaseCleanupTestState.Releases = @( + [pscustomobject]@{ tagName = 'v1.2.3'; isPrerelease = $false; isLatest = $true } + [pscustomobject]@{ tagName = 'v0.1.0'; isPrerelease = $false; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.0.0-featuretest'; isPrerelease = $false; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-featuretest'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-featuretest.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-featuretest.20261102.2'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-featuretestextra.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-otherfeaturetest.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-other.1+featuretest'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-other.1'; isPrerelease = $true; isLatest = $false } + ) + $script:deletedTags = [System.Collections.Generic.List[string]]::new() + $global:ReleaseCleanupTestState.DeletedTags = $script:deletedTags + $global:LASTEXITCODE = 0 + + # Destructive GitHub and git boundaries are simulated; no real releases or tags are changed. + Mock -CommandName Install-PSResource {} + Mock -CommandName Import-Module {} + Mock -CommandName git {} + Mock -CommandName gh { + if ($args[0] -eq 'repo' -and $args[1] -eq 'view') { + return '{"defaultBranchRef":{"name":"main"}}' + } + if ($args[0] -eq 'release' -and $args[1] -eq 'list') { + return ConvertTo-Json -InputObject $global:ReleaseCleanupTestState.Releases + } + if ($args[0] -eq 'release' -and $args[1] -eq 'delete') { + $global:ReleaseCleanupTestState.DeletedTags.Add($args[2]) + return + } + if ($args[0] -eq 'release' -and $args[1] -eq 'create') { + return 'https://example.invalid/releases/test' + } + if ($args[0] -eq 'pr' -and $args[1] -eq 'comment') { + return + } + throw "Unexpected gh command: $($args -join ' ')" + } + } + + AfterEach { + foreach ($name in $script:previousEnvironment.Keys) { + [Environment]::SetEnvironmentVariable($name, $script:previousEnvironment[$name]) + } + $global:LASTEXITCODE = $script:previousExitCode + } + + It 'Does not delete any release for the empty identifier from branch ""' -ForEach @( + @{ BranchName = '' } + @{ BranchName = '_' } + @{ BranchName = '-' } + @{ BranchName = '---___' } + ) { + $script:event.pull_request.head.ref = $BranchName + $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH + + & $script:mainPath | Out-Null + + $script:deletedTags.Count | Should -Be 0 + } + + It 'Deletes only exact branch prereleases for short or common branch ""' -ForEach @( + @{ BranchName = 'v1'; ExpectedTag = 'v1.2.4-v1.1' } + @{ BranchName = '1'; ExpectedTag = 'v1.2.4-1.1' } + @{ BranchName = '0'; ExpectedTag = 'v1.2.4-0.1' } + ) { + $script:event.pull_request.head.ref = $BranchName + $global:ReleaseCleanupTestState.Releases += @( + [pscustomobject]@{ tagName = $ExpectedTag; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = "v1.2.4-${BranchName}extra.1"; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = "v1.2.4-other${BranchName}.1"; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = "v1.0.0-${BranchName}"; isPrerelease = $false; isLatest = $false } + ) + $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH + + & $script:mainPath | Out-Null + + $script:deletedTags.Count | Should -Be 1 + $script:deletedTags[0] | Should -BeExactly $ExpectedTag + } + + It 'Cleans up plain, incremental, and date-based branch prereleases after close or merge' -ForEach @( + @{ Merged = $false } + @{ Merged = $true } + ) { + $script:event.pull_request.merged = $Merged + $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH + + & $script:mainPath | Out-Null + + $script:deletedTags.Count | Should -Be 3 + $script:deletedTags[0] | Should -BeExactly 'v1.2.4-featuretest' + $script:deletedTags[1] | Should -BeExactly 'v1.2.4-featuretest.1' + $script:deletedTags[2] | Should -BeExactly 'v1.2.4-featuretest.20261102.2' + if ($Merged) { + Should -Invoke -CommandName gh -Times 1 -Exactly -ParameterFilter { + $args[0] -eq 'release' -and $args[1] -eq 'create' -and $args[2] -eq 'v1.2.4' + } + } + } + + It 'Honors AutoCleanup being disabled' { + $env:PSMODULE_AUTO_RELEASE_INPUT_AutoCleanup = 'false' + $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH + + & $script:mainPath | Out-Null + + $script:deletedTags.Count | Should -Be 0 + } + + It 'Does not clean up while the pull request is open' { + $script:event.action = 'synchronize' + $script:event.pull_request.state = 'open' + $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH + + & $script:mainPath | Out-Null + + $script:deletedTags.Count | Should -Be 0 + } + + It 'Honors a custom literal version prefix' { + $env:PSMODULE_AUTO_RELEASE_INPUT_VersionPrefix = 'release.v' + $global:ReleaseCleanupTestState.Releases += @( + [pscustomobject]@{ tagName = 'release.v1.2.4-featuretest.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'releaseXv1.2.4-featuretest.1'; isPrerelease = $true; isLatest = $false } + ) + $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH + + & $script:mainPath | Out-Null + + $script:deletedTags.Count | Should -Be 1 + $script:deletedTags[0] | Should -BeExactly 'release.v1.2.4-featuretest.1' + } + + It 'Supports tags without a version prefix' { + $env:PSMODULE_AUTO_RELEASE_INPUT_VersionPrefix = '' + $global:ReleaseCleanupTestState.Releases += @( + [pscustomobject]@{ tagName = '1.2.4-featuretest.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = '1.2.3'; isPrerelease = $false; isLatest = $false } + ) + $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH + + & $script:mainPath | Out-Null + + $script:deletedTags.Count | Should -Be 1 + $script:deletedTags[0] | Should -BeExactly '1.2.4-featuretest.1' + } +} + +AfterAll { + $global:ReleaseCleanupTestState = $script:previousTestState +} From ea0c3b1523f4483234db760cc30f72781b04cc08 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 3 Nov 2026 00:09:18 +0100 Subject: [PATCH 2/3] Restrict cleanup to exact branch prereleases --- README.md | 16 ++++++++++++ src/main.ps1 | 13 +++++++++- tests/ReleaseCleanup.Tests.ps1 | 47 +++++++++++++++++++++++++++++----- 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9bcd513..0b65f97 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,11 @@ The following labels will inform the action what kind of release to create: When a pull request is closed, the action will create a release based on the labels and clean up any previous prereleases that were created. +Cleanup only deletes releases marked as prereleases whose version tags match the configured `VersionPrefix` and the exact sanitized +branch name as the first prerelease identifier. Plain, incremental, and date-based branch prereleases are supported. +Stable releases and unrelated branch prereleases are never selected. If removing non-alphanumeric characters leaves the branch name empty +(for example, `_` or `-`), cleanup is skipped with a warning. Cleanup also runs when an unmerged pull request is closed. + > [!NOTE] > The labels can be configured using the `MajorLabels`, `MinorLabels` and `PatchLabels` parameters/settings in the configuration file to trigger > on other labels. @@ -116,3 +121,14 @@ permissions: contents: write # Required to create releases pull-requests: write # Required to create comments on the PRs ``` + +## Local cleanup tests + +With PowerShell 7, Pester 6, and PSSemVer installed, run: + +```powershell +Invoke-Pester -Path .\tests\ReleaseCleanup.Tests.ps1 -Output Detailed +``` + +The tests run the action script with isolated pull request events and release fixtures. +GitHub and git commands are mocked so no real releases or tags are created or deleted. diff --git a/src/main.ps1 b/src/main.ps1 index 089a75f..50f8424 100644 --- a/src/main.ps1 +++ b/src/main.ps1 @@ -365,7 +365,18 @@ if ($createPrerelease -or $createRelease -or $whatIf) { } LogGroup 'List prereleases using the same name' { - $prereleasesToCleanup = $releases | Where-Object { $_.tagName -like "*$prereleaseName*" } + $prereleasesToCleanup = @() + if ([string]::IsNullOrEmpty($prereleaseName)) { + Write-Warning "Skipping prerelease cleanup: branch [$prHeadRef] has no alphanumeric prerelease name." + } else { + # Match the first SemVer prerelease identifier, not a substring of the tag. + $escapedVersionPrefix = [regex]::Escape($versionPrefix) + $escapedPrereleaseName = [regex]::Escape($prereleaseName) + $prereleaseTagPattern = "^${escapedVersionPrefix}[0-9]+\.[0-9]+\.[0-9]+-${escapedPrereleaseName}(?:\.|$)" + $prereleasesToCleanup = $releases | Where-Object { + $_.isPrerelease -eq $true -and $_.tagName -match $prereleaseTagPattern + } + } $prereleasesToCleanup | Select-Object -Property name, publishedAt, isPrerelease, isLatest | Format-Table | Out-String } diff --git a/tests/ReleaseCleanup.Tests.ps1 b/tests/ReleaseCleanup.Tests.ps1 index 38620b9..0521e75 100644 --- a/tests/ReleaseCleanup.Tests.ps1 +++ b/tests/ReleaseCleanup.Tests.ps1 @@ -134,6 +134,10 @@ Describe 'Branch prerelease cleanup' { [pscustomobject]@{ tagName = 'v1.2.4-otherfeaturetest.1'; isPrerelease = $true; isLatest = $false } [pscustomobject]@{ tagName = 'v1.2.4-other.1+featuretest'; isPrerelease = $true; isLatest = $false } [pscustomobject]@{ tagName = 'v1.2.4-other.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'notversion-featuretest.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4.5-featuretest.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-featuretest.3'; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-featuretest.4'; isPrerelease = $null; isLatest = $false } ) $script:deletedTags = [System.Collections.Generic.List[string]]::new() $global:ReleaseCleanupTestState.DeletedTags = $script:deletedTags @@ -171,13 +175,18 @@ Describe 'Branch prerelease cleanup' { $global:LASTEXITCODE = $script:previousExitCode } - It 'Does not delete any release for the empty identifier from branch ""' -ForEach @( - @{ BranchName = '' } - @{ BranchName = '_' } - @{ BranchName = '-' } - @{ BranchName = '---___' } + It 'Does not delete any release for the empty identifier from branch "" (merged: )' -ForEach @( + @{ BranchName = ''; Merged = $false } + @{ BranchName = '_'; Merged = $false } + @{ BranchName = '-'; Merged = $false } + @{ BranchName = '---___'; Merged = $false } + @{ BranchName = ''; Merged = $true } + @{ BranchName = '_'; Merged = $true } + @{ BranchName = '-'; Merged = $true } + @{ BranchName = '---___'; Merged = $true } ) { $script:event.pull_request.head.ref = $BranchName + $script:event.pull_request.merged = $Merged $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH & $script:mainPath | Out-Null @@ -205,7 +214,7 @@ Describe 'Branch prerelease cleanup' { $script:deletedTags[0] | Should -BeExactly $ExpectedTag } - It 'Cleans up plain, incremental, and date-based branch prereleases after close or merge' -ForEach @( + It 'Cleans up plain, incremental, and date-based branch prereleases (merged: )' -ForEach @( @{ Merged = $false } @{ Merged = $true } ) { @@ -271,6 +280,32 @@ Describe 'Branch prerelease cleanup' { $script:deletedTags.Count | Should -Be 1 $script:deletedTags[0] | Should -BeExactly '1.2.4-featuretest.1' } + + It 'Previews only intended deletions in WhatIf mode for branch ""' -ForEach @( + @{ BranchName = '_'; ExpectedTags = @() } + @{ BranchName = '1'; ExpectedTags = @('v1.2.4-1.1') } + @{ + BranchName = 'feature/test' + ExpectedTags = @('v1.2.4-featuretest', 'v1.2.4-featuretest.1', 'v1.2.4-featuretest.20261102.2') + } + ) { + $env:PSMODULE_AUTO_RELEASE_INPUT_WhatIf = 'true' + $script:event.pull_request.head.ref = $BranchName + $global:ReleaseCleanupTestState.Releases += [pscustomobject]@{ + tagName = 'v1.2.4-1.1'; isPrerelease = $true; isLatest = $false + } + $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH + + $output = & $script:mainPath + + $previews = @($output | Where-Object { $_ -like 'WhatIf: gh release delete *' }) + $previews.Count | Should -Be $ExpectedTags.Count + for ($index = 0; $index -lt $ExpectedTags.Count; $index++) { + $previews[$index] | Should -BeExactly "WhatIf: gh release delete $($ExpectedTags[$index]) --cleanup-tag --yes" + } + $script:deletedTags.Count | Should -Be 0 + Should -Invoke -CommandName git -Times 0 -Exactly + } } AfterAll { From 9f473cb39d3de649546c62abe497f531345583d5 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 3 Nov 2026 09:11:51 +0100 Subject: [PATCH 3/3] Match cleanup branch identifiers and prefixes case-sensitively --- README.md | 3 ++- src/main.ps1 | 2 +- tests/ReleaseCleanup.Tests.ps1 | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0b65f97..78c3d45 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,8 @@ The following labels will inform the action what kind of release to create: When a pull request is closed, the action will create a release based on the labels and clean up any previous prereleases that were created. Cleanup only deletes releases marked as prereleases whose version tags match the configured `VersionPrefix` and the exact sanitized -branch name as the first prerelease identifier. Plain, incremental, and date-based branch prereleases are supported. +branch name as the first prerelease identifier. Both matches are case-sensitive. +Plain, incremental, and date-based branch prereleases are supported. Stable releases and unrelated branch prereleases are never selected. If removing non-alphanumeric characters leaves the branch name empty (for example, `_` or `-`), cleanup is skipped with a warning. Cleanup also runs when an unmerged pull request is closed. diff --git a/src/main.ps1 b/src/main.ps1 index 50f8424..e620e20 100644 --- a/src/main.ps1 +++ b/src/main.ps1 @@ -374,7 +374,7 @@ LogGroup 'List prereleases using the same name' { $escapedPrereleaseName = [regex]::Escape($prereleaseName) $prereleaseTagPattern = "^${escapedVersionPrefix}[0-9]+\.[0-9]+\.[0-9]+-${escapedPrereleaseName}(?:\.|$)" $prereleasesToCleanup = $releases | Where-Object { - $_.isPrerelease -eq $true -and $_.tagName -match $prereleaseTagPattern + $_.isPrerelease -eq $true -and $_.tagName -cmatch $prereleaseTagPattern } } $prereleasesToCleanup | Select-Object -Property name, publishedAt, isPrerelease, isLatest | Format-Table | Out-String diff --git a/tests/ReleaseCleanup.Tests.ps1 b/tests/ReleaseCleanup.Tests.ps1 index 0521e75..5ccc546 100644 --- a/tests/ReleaseCleanup.Tests.ps1 +++ b/tests/ReleaseCleanup.Tests.ps1 @@ -234,6 +234,26 @@ Describe 'Branch prerelease cleanup' { } } + It 'Matches branch identifiers and version prefixes case-sensitively for ""' -ForEach @( + @{ BranchName = 'feature/test'; ExpectedTag = 'v1.2.4-featuretest.1' } + @{ BranchName = 'Feature/Test'; ExpectedTag = 'v1.2.4-FeatureTest.1' } + ) { + $script:event.pull_request.head.ref = $BranchName + $global:ReleaseCleanupTestState.Releases = @( + [pscustomobject]@{ tagName = 'v1.2.3'; isPrerelease = $false; isLatest = $true } + [pscustomobject]@{ tagName = 'v1.2.4-featuretest.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'v1.2.4-FeatureTest.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'V1.2.4-featuretest.1'; isPrerelease = $true; isLatest = $false } + [pscustomobject]@{ tagName = 'V1.2.4-FeatureTest.1'; isPrerelease = $true; isLatest = $false } + ) + $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH + + & $script:mainPath | Out-Null + + $script:deletedTags.Count | Should -Be 1 + $script:deletedTags[0] | Should -BeExactly $ExpectedTag + } + It 'Honors AutoCleanup being disabled' { $env:PSMODULE_AUTO_RELEASE_INPUT_AutoCleanup = 'false' $script:event | ConvertTo-Json -Depth 5 | Set-Content -Path $env:GITHUB_EVENT_PATH